/// <summary>
        /// Generates a TMS constraint restricting the value of a decision variable to a predefined one
        /// </summary>
        /// <param name="domain">The domain of the decision variable</param>
        /// <param name="dVarName">The decision variable</param>
        /// <param name="value">The value that the decision value is restricted to have</param>
        /// <returns>The TMS constraint</returns>
        internal static TmsConstraint GenerateSingleValueConstraint(GKODomainAbstract domain, string dVarName, string value)
        {
            TmsConstraint tmsConstraint = new TmsConstraint();

            tmsConstraint.ConstraintType = domain.GetIndividualValueCTName(value);
            tmsConstraint.VariableTuple  = new List <string>()
            {
                dVarName
            };

            return(tmsConstraint);
        }
Exemplo n.º 2
0
        ///// <summary>
        ///// Create all constraint types corresponding to the subsets of the relations in a relation family
        ///// </summary>
        ///// <param name="calculus">The relation family</param>
        //private void CreateCTForPowerset(RelationFamily calculus)
        //{
        //    //GKOConstraintType constraintType;
        //    //GKODomainAbstract relationsDomain;
        //    //GKODomainAbstract powerSetIxDomain;
        //    //bool enableSoftConstraints = this.structuralReasonerOptions.SoftConstraintsEnabled;

        //    //if (calculus.Name == RelationFamilyNames.MetricRelationsName)
        //    //{
        //    //    throw new NotSupportedException("The metric relations do not support this operation.");
        //    //}

        //    //relationsDomain = StructuralRelationsManager.GetDomain(calculus.GetTmsRelationsDomainName());
        //    //powerSetIxDomain = this.CalculiPwSetIxDomain;

        //    //constraintType = new GKOConstraintType();
        //    //constraintType.Id = calculus.GetPwSetCTName();
        //    //constraintType.Name = calculus.GetPwSetCTName();
        //    //constraintType.Signature = new List<GKODomainAbstract>() { powerSetIxDomain, relationsDomain };
        //    //constraintType.Tuples = new List<List<string>>();
        //    //if (enableSoftConstraints)
        //    //{
        //    //    constraintType.Signature.Add(this.BoolDomain);
        //    //}

        //    //// The name of the constraint is labeled by the integer representation of the included relations
        //    //// e.g. set 17 = 10001, i.e. relations 0 and 3 are included in the constraint with label 17
        //    //for (int setIx = 1; setIx < Math.Pow(2, calculus.Relations.Count); setIx++)
        //    //{
        //    //    List<BinaryRelation> includedRelations = new List<BinaryRelation>();

        //    //    for (int i = 0; i < calculus.Relations.Count; i++)
        //    //    {
        //    //        if ((setIx & (1 << i)) != 0)
        //    //        {
        //    //            //n-th bit is set, so we include the n-th relation
        //    //            includedRelations.Add(calculus.Relations[i]);
        //    //        }
        //    //    }

        //    //    // When soft constraints are enabled the constraint types should be adjusted accordingly
        //    //    if (enableSoftConstraints)
        //    //    {
        //    //        List<BinaryRelation> excludedReltions = calculus.Relations.Except(includedRelations).ToList();

        //    //        foreach (var rel in includedRelations)
        //    //        {
        //    //            constraintType.Tuples.Add(new List<string>() { setIx.ToString(), rel.Name, TmsManager.TrueValue });
        //    //        }

        //    //        // Adding the non-satisfied tuples: *, 0
        //    //        // ct.Tuples.Add(new List<string>() { TmsManager.WildcardValue, TmsManager.FalseValue });
        //    //        foreach (var rel in excludedReltions)
        //    //        {
        //    //            constraintType.Tuples.Add(new List<string>() { setIx.ToString(), rel.Name, TmsManager.FalseValue });
        //    //        }
        //    //    }
        //    //    else
        //    //    {
        //    //        foreach (var rel in includedRelations)
        //    //        {
        //    //            constraintType.Tuples.Add(new List<string>() { setIx.ToString(), rel.Name });
        //    //        }
        //    //    }
        //    //}

        //    //this.ConstraintTypes.Add(constraintType.Name, constraintType.CreateIConstraintType(cdaStarTms, this.Domains));
        //}

        /// <summary>
        /// Creates the metric constraint types
        /// </summary>
        /// <returns></returns>
        private void CreateMetricRelConstrTypes()
        {
            GKOIntDomain          metricDomain    = this.MetricDomain;
            GKODomainAbstract     satisfiedDomain = this.BoolDomain;
            GKOConstraintType     constraintType;
            List <List <string> > tuples;
            // Legacy variable. Now it should be always true
            bool enableSoftConstraints = true;

            // Creating the "Greater than" (a>b) constraint type
            constraintType = new GKOConstraintType()
            {
                Id   = CTNameGreaterThan,
                Name = CTNameGreaterThan
            };
            constraintType.Signature = new List <GKODomainAbstract>()
            {
                metricDomain, metricDomain
            };
            if (enableSoftConstraints)
            {
                constraintType.Signature.Add(satisfiedDomain);
            }

            tuples = new List <List <string> >();
            for (int a = metricDomain.MinValue; a < metricDomain.MaxValue + 1; a += metricDomain.StepWidth)
            {
                if (enableSoftConstraints)
                {
                    for (int b = metricDomain.MinValue; b < metricDomain.MaxValue + 1; b += metricDomain.StepWidth)
                    {
                        // in some cases the non-satisfied tuples are added: *, *, 0
                        // tuples.Add(new List<string>() { TmsManager.WildcardValue, TmsManager.WildcardValue, TmsManager.FalseValue });
                        tuples.Add(new List <string>()
                        {
                            a.ToString(), b.ToString(), a > b ? TrueValue : FalseValue
                        });
                    }
                }
                else
                {
                    for (int b = metricDomain.MinValue; b < a; b += metricDomain.StepWidth)
                    {
                        tuples.Add(new List <string>()
                        {
                            a.ToString(), b.ToString()
                        });
                    }
                }
            }

            constraintType.Tuples = tuples;
            // Adds the constraint type to the CS3
            this.ConstraintTypes.Add(constraintType.Name, constraintType.CreateIConstraintType(cdaStarTms, this.Domains));

            // Creating the "Greater or equals" (a>=b) constraint type
            constraintType = new GKOConstraintType()
            {
                Id   = CTNameGreaterOrEquals,
                Name = CTNameGreaterOrEquals
            };
            constraintType.Signature = new List <GKODomainAbstract>()
            {
                metricDomain, metricDomain
            };
            if (enableSoftConstraints)
            {
                constraintType.Signature.Add(satisfiedDomain);
            }

            tuples = new List <List <string> >();
            for (int a = metricDomain.MinValue; a < metricDomain.MaxValue + 1; a += metricDomain.StepWidth)
            {
                if (enableSoftConstraints)
                {
                    for (int b = metricDomain.MinValue; b < metricDomain.MaxValue + 1; b += metricDomain.StepWidth)
                    {
                        // in some cases the non-satisfied tuples are added: *, *, 0
                        // tuples.Add(new List<string>() { TmsManager.WildcardValue, TmsManager.WildcardValue, TmsManager.FalseValue });
                        tuples.Add(new List <string>()
                        {
                            a.ToString(), b.ToString(), a >= b ? TrueValue : FalseValue
                        });
                    }
                }
                else
                {
                    for (int b = metricDomain.MinValue; b <= a; b += metricDomain.StepWidth)
                    {
                        tuples.Add(new List <string>()
                        {
                            a.ToString(), b.ToString()
                        });
                    }
                }
            }

            constraintType.Tuples = tuples;
            // Adds the constraint type to the CS3
            this.ConstraintTypes.Add(constraintType.Name, constraintType.CreateIConstraintType(cdaStarTms, this.Domains));

            // Creating the "Equals" (a=b) constraint type
            constraintType = new GKOConstraintType()
            {
                Id   = CTNameEquals,
                Name = CTNameEquals
            };
            constraintType.Signature = new List <GKODomainAbstract>()
            {
                metricDomain, metricDomain
            };
            if (enableSoftConstraints)
            {
                constraintType.Signature.Add(satisfiedDomain);
            }

            tuples = new List <List <string> >();
            for (int a = metricDomain.MinValue; a < metricDomain.MaxValue + 1; a += metricDomain.StepWidth)
            {
                if (enableSoftConstraints)
                {
                    for (int b = metricDomain.MinValue; b < metricDomain.MaxValue + 1; b += metricDomain.StepWidth)
                    {
                        // in some cases the non-satisfied tuples are added: *, *, 0
                        // tuples.Add(new List<string>() { TmsManager.WildcardValue, TmsManager.WildcardValue, TmsManager.FalseValue });
                        tuples.Add(new List <string>()
                        {
                            a.ToString(), b.ToString(), a == b ? TmsManager.TrueValue : TmsManager.FalseValue
                        });
                    }
                }
                else
                {
                    tuples.Add(new List <string>()
                    {
                        a.ToString(), a.ToString()
                    });
                }
            }

            constraintType.Tuples = tuples;
            // Adds the constraint type to the CS3
            this.ConstraintTypes.Add(constraintType.Name, constraintType.CreateIConstraintType(cdaStarTms, this.Domains));

            // Creating the "Not equals" (a!=b) constraint type
            constraintType = new GKOConstraintType()
            {
                Id   = CTNameNotEquals,
                Name = CTNameNotEquals
            };
            constraintType.Signature = new List <GKODomainAbstract>()
            {
                metricDomain, metricDomain
            };
            if (enableSoftConstraints)
            {
                constraintType.Signature.Add(satisfiedDomain);
            }

            tuples = new List <List <string> >();
            for (int a = metricDomain.MinValue; a < metricDomain.MaxValue + 1; a += metricDomain.StepWidth)
            {
                if (enableSoftConstraints)
                {
                    for (int b = metricDomain.MinValue; b < metricDomain.MaxValue + 1; b += metricDomain.StepWidth)
                    {
                        // in some cases the non-satisfied tuples are added: *, *, 0
                        // tuples.Add(new List<string>() { TmsManager.WildcardValue, TmsManager.WildcardValue, TmsManager.FalseValue });
                        tuples.Add(new List <string>()
                        {
                            a.ToString(), b.ToString(), a != b ? TrueValue : FalseValue
                        });
                    }
                }
                else
                {
                    for (int b = metricDomain.MinValue; b < metricDomain.MaxValue + 1; b += metricDomain.StepWidth)
                    {
                        if (a != b)
                        {
                            tuples.Add(new List <string>()
                            {
                                a.ToString(), b.ToString()
                            });
                        }
                    }
                }
            }

            constraintType.Tuples = tuples;
            // Adds the constraint type to the CS3
            this.ConstraintTypes.Add(constraintType.Name, constraintType.CreateIConstraintType(cdaStarTms, this.Domains));

            // Creating the "Plus" (a + b = c) constraint type
            constraintType = new GKOConstraintType()
            {
                Id   = CTNamePlus,
                Name = CTNamePlus
            };
            constraintType.Signature = new List <GKODomainAbstract>()
            {
                metricDomain, metricDomain, metricDomain
            };
            if (enableSoftConstraints)
            {
                constraintType.Signature.Add(satisfiedDomain);
            }

            tuples = new List <List <string> >();
            for (int a = metricDomain.MinValue; a < metricDomain.MaxValue + 1; a += metricDomain.StepWidth)
            {
                if (enableSoftConstraints)
                {
                    for (int b = metricDomain.MinValue; b < metricDomain.MaxValue + 1; b += metricDomain.StepWidth)
                    {
                        for (int c = metricDomain.MinValue; c < metricDomain.MaxValue + 1; c += metricDomain.StepWidth)
                        {
                            // in some cases the non-satisfied tuples are added: *, *, *, 0
                            // tuples.Add(new List<string>() { TmsManager.WildcardValue, TmsManager.WildcardValue, TmsManager.WildcardValue, TmsManager.FalseValue });
                            tuples.Add(new List <string>()
                            {
                                a.ToString(), b.ToString(), c.ToString(), (a + b) == c ? TrueValue : FalseValue
                            });
                        }
                    }
                }
                else
                {
                    for (int b = metricDomain.MinValue; a + b < metricDomain.MaxValue + 1; b += metricDomain.StepWidth)
                    {
                        tuples.Add(new List <string>()
                        {
                            a.ToString(), b.ToString(), (a + b).ToString()
                        });
                    }
                }
            }

            constraintType.Tuples = tuples;
            // Adds the constraint type to the CS3
            this.ConstraintTypes.Add(constraintType.Name, constraintType.CreateIConstraintType(cdaStarTms, this.Domains));
        }
Exemplo n.º 3
0
        ///// <summary>
        ///// Generates the domain used as index for the powerset of the enabled calculi
        ///// </summary>
        ///// <returns></returns>
        //private GKOIntDomain GenerateCalculiPwSetIxDomain()
        //{
        //    GKOIntDomain domain = new GKOIntDomain()
        //    {
        //        Id = DomainNameCalculiPwSetIx,
        //        Name = DomainNameCalculiPwSetIx,
        //        StepWidth = 1,
        //        MinValue = 0,
        //        MaxValue = (int)Math.Pow(2, StructuralRelationsManager.RelationFamilies.Max(x => x.Relations.Count))
        //    };

        //    return domain;
        //}

        #region Constraint Types

        /// <summary>
        /// Creates constraint types in the CS3, each new CT contains only one value from a domain.
        /// NOTE: These constraint types are used to allow only one value to be selected for a variable.
        /// </summary>
        /// <param name="domain">The domain to create constraint types for</param>
        /// <param name="addSoft">Specifies whether to add a softness variable at the end</param>
        private void CreateIndividualCTForDomain(GKODomainAbstract domain, bool addSoft)
        {
            List <GKOConstraintType> constraintTypes = new List <GKOConstraintType>();
            List <string>            domainValues    = new List <string>();

            if (domain is GKOIntDomain)
            {
                GKOIntDomain domainTemp = domain as GKOIntDomain;

                for (int i = domainTemp.MinValue; i < domainTemp.MaxValue + 1; i += domainTemp.StepWidth)
                {
                    domainValues.Add(i.ToString());
                }
            }
            else if (domain is GKODomain)
            {
                GKODomain domainTemp = domain as GKODomain;

                foreach (var value in domainTemp.Values)
                {
                    domainValues.Add(value);
                }
            }

            foreach (var value in domainValues)
            {
                GKOConstraintType ct = new GKOConstraintType()
                {
                    Id        = domain.GetIndividualValueCTName(value),
                    Name      = domain.GetIndividualValueCTName(value),
                    Signature = new List <GKODomainAbstract>()
                    {
                        domain
                    },
                    Tuples = new List <List <string> >()
                    {
                        new List <string>()
                        {
                            value
                        }
                    }
                };

                if (addSoft)
                {
                    ct.Signature.Add(this.BoolDomain);
                    ct.Tuples[0].Add(TrueValue);
                    foreach (var excludedValue in domainValues)
                    {
                        ct.Tuples.Add(new List <string>()
                        {
                            excludedValue, FalseValue
                        });
                    }
                }

                constraintTypes.Add(ct);
            }

            constraintTypes.ForEach(x => this.ConstraintTypes.Add(x.Name, x.CreateIConstraintType(cdaStarTms, this.Domains)));
        }
Exemplo n.º 4
0
 internal TmsDecisionVariable(GKODomainAbstract domain, string name)
 {
     this.Domain        = domain;
     this.Name          = name;
     this.UtilityFactor = 0;
 }
        internal SolutionDataMetric SolveMetric(StructuralReasonerOptions options, TmsManager tms)
        {
            SolutionDataMetric solutionInformation = new SolutionDataMetric(this);

            solutionInformation.StartTime = DateTime.Now;

            //try
            //{
            if (options.MetricReasoningAlgorithm == MetricReasoningAlgorithm.Tcsp)
            {
                Tcsp tcsp     = new Tcsp(this);
                Stp  solution = null;

                if (this.Edges.Values.Any(x => x.AllConstraints.Any(y => !y.OwningTreeRoot.IsValidForTcsp)))
                {
                    solutionInformation.Log.AddItem(LogType.Error, string.Format("Disjunctive domain constraints are not allowed in TCSP!", this.UId));
                }
                else
                {
                    solutionInformation.Log.AddItem(LogType.Info, string.Format("Solving metric network {0} with TCSP", this.UId));
                    solution = tcsp.Solve();
                    solutionInformation.Log.AddItem(LogType.Info, string.Format("TCSP solving process complete ({0} ms)", (DateTime.Now - solutionInformation.StartTime).TotalMilliseconds));
                }

                if (solution != null && solution.IsConsistent)
                {
                    solutionInformation.SolutionFound = true;
                    solutionInformation.Solution      = solution.GetSolution();
                }
                else
                {
                    solutionInformation.SolutionFound = false;
                }
            }
            else
            {
                List <TmsDecisionVariable>            tmsVariables            = new List <TmsDecisionVariable>(); // Normal TMS variables
                List <TmsDecisionVariable>            tmsUtilityVariables     = new List <TmsDecisionVariable>(); // Utility TMS variables
                List <TmsConstraint>                  tmsConstraints          = new List <TmsConstraint>();
                HashSet <ConfigurationConstraintTree> relevantConstraintTrees = new HashSet <ConfigurationConstraintTree>();
                GKODomainAbstract satisfiedDomain = tms.BoolDomain;
                GKODomainAbstract metricDomain    = tms.MetricDomain;

                solutionInformation.Log.AddItem(LogType.Info, string.Format("Solving metric network {0} with CDA* started", this.UId));

                solutionInformation.Stopwatch.Start();

                // Adding all node decision variables
                foreach (var node in this.Nodes)
                {
                    // The biginning of time is not relevant for the CDA*
                    if (node != null)
                    {
                        tmsVariables.Add(new TmsDecisionVariable(metricDomain, node.GetDVarName()));
                    }
                }

                // Processing configuration constraints in edges
                foreach (var edge in this.Edges.Values)
                {
                    foreach (var constraint in edge.Constraints)
                    {
                        List <MetricRelationPart> metricParts   = constraint.RelationParts.Where(x => x is MetricRelationPart).Select(x => (MetricRelationPart)x).ToList();
                        TmsConstraint             tmsConstraint = new TmsConstraint();

                        // Creating the TMS constraint implied by the configuration constraint
                        tmsConstraints.AddRange(constraint.AllowedRelations[0].GetTmsConstraints(constraint, edge));

                        // Each metric part in the constraint has its own decision variable which has to be created
                        // Additionally, there is a hard constraint restricting the value of the variable to the single selected values
                        foreach (var metricPart in metricParts)
                        {
                            // If the value of the metric part is already used it will have the same decision variable name
                            if (!tmsVariables.Any(x => x.Name == metricPart.GetDVarName()))
                            {
                                tmsVariables.Add(new TmsDecisionVariable(metricDomain, metricPart.GetDVarName()));
                                tmsConstraints.Add(TmsConstraint.GenerateSingleValueConstraint(StructuralRelationsManager.MetricDomain, metricPart.GetDVarName(), metricPart.GetValue()));
                            }
                        }
                    }

                    // Adding all configuration trees, from which the configuration constraints in edge are derived, in a set. This set is used later to generate the constraints combination and utility TMS variables and TMS constraints
                    foreach (var constraint in edge.AllConstraints)
                    {
                        relevantConstraintTrees.Add(constraint.OwningTreeRoot);
                    }
                }

                // Generating the constraints and variables for each constraint tree
                foreach (var constraintTree in relevantConstraintTrees)
                {
                    tmsConstraints.AddRange(constraintTree.GenerateTmsConstraints());
                    tmsVariables.AddRange(constraintTree.GetNonUtilityDecisionVariables());
                    tmsUtilityVariables.AddRange(constraintTree.GetUtilityDecisionVariables());
                }

                // Since some decision variables can be duplicated, we have to find the distinct ones
                // Additionally, for utility variables their utility factor has to be recalculated as needed
                tmsVariables        = TmsDecisionVariable.GetDistinctVariables(tmsVariables, false);
                tmsUtilityVariables = TmsDecisionVariable.GetDistinctVariables(tmsUtilityVariables, true);

                // It is possible that a variable is defined in both lists so it has to be removed from this one
                tmsVariables = tmsVariables.Where(x => !tmsUtilityVariables.Any(y => x.Name == y.Name)).ToList();

                solutionInformation.Stopwatch.Stop();
                solutionInformation.Log.AddItem(LogType.Info, string.Format("TMS variables and constraints generated ({0} ms)", solutionInformation.Stopwatch.ElapsedMilliseconds));

                solutionInformation.Log.AddItem(LogType.Info, string.Format("TMS solution process started", solutionInformation.Stopwatch.ElapsedMilliseconds));
                solutionInformation.Log.AddItem(LogType.Info, string.Format("Variables (excl. soft): {0}", tmsVariables.Count));
                solutionInformation.Log.AddItem(LogType.Info, string.Format("Variables for soft constraints: {0}", tmsUtilityVariables.Count));
                solutionInformation.Log.AddItem(LogType.Info, string.Format("Constraints: {0}", tmsConstraints.Count));

                solutionInformation.Stopwatch.Restart();

                this.SolveWithTms(solutionInformation, tms, tmsVariables, tmsUtilityVariables, tmsConstraints);

                solutionInformation.Stopwatch.Stop();
                solutionInformation.Log.AddItem(LogType.Info, string.Format("TMS solution process complete ({0} ms)", solutionInformation.Stopwatch.ElapsedMilliseconds));
            }
            //}
            //catch (Exception e)
            //{
            //    solutionInformation.Log.AddItem(LogType.Error, e.ToString());
            //}

            solutionInformation.SolutionFinished();

            return(solutionInformation);
        }
        internal SolutionDataQualitative SolveQualitative(StructuralReasonerOptions options, TmsManager tms)
        {
            SolutionDataQualitative               solutionInformation     = new SolutionDataQualitative(this);
            List <TmsDecisionVariable>            tmsVariables            = new List <TmsDecisionVariable>(); // Normal TMS variables
            List <TmsDecisionVariable>            tmsUtilityVariables     = new List <TmsDecisionVariable>(); // Utility TMS variables
            List <TmsConstraint>                  tmsConstraints          = new List <TmsConstraint>();
            HashSet <ConfigurationConstraintTree> relevantConstraintTrees = new HashSet <ConfigurationConstraintTree>();
            GKODomainAbstract satisfiedDomain = tms.BoolDomain;
            GKODomainAbstract calculusDomain  = tms.CalculiDomains.Single(x => x.Name == this.RelationFamily.GetTmsRelationsDomainName());

            solutionInformation.StartTime = DateTime.Now;

            //try
            //{
            solutionInformation.Stopwatch.Start();
            // For each edge adds the decion variables of the edge and generates the TMS constraints for each configuration constraint
            foreach (var edge in this.Edges.Values)
            {
                QualitativeEdge qualEdge = (edge as QualitativeEdge);
                string          edgeDVar = qualEdge.GetDVarName();

                // Adding the edge decision variable
                tmsVariables.Add(new TmsDecisionVariable(calculusDomain, edgeDVar));

                // Adding the bool dvar for each relation used in a constraint of the edge
                // They are mapped to the edge dvar later with a constraint
                foreach (var relation in edge.Constraints.SelectMany(x => x.AllowedRelations).Distinct())
                {
                    tmsVariables.Add(new TmsDecisionVariable(satisfiedDomain, qualEdge.GetDVarName((QualitativeRelation)relation)));
                }

                foreach (var constraint in edge.Constraints)
                {
                    // ToDo: Make the GetTmsConstraints more consistent - now it is actually working on a constraint lvl for qualitative relations
                    // Adding the TMS constraints derivde by the relation
                    tmsConstraints.AddRange(constraint.AllowedRelations[0].GetTmsConstraints(constraint, edge));
                }

                // Adding all configuration trees, from which the configuration constraints in edge are derived, in a set. This set is used later to generate the constraints combination and utility TMS variables and TMS constraints
                foreach (var constraint in edge.AllConstraints)
                {
                    relevantConstraintTrees.Add(constraint.OwningTreeRoot);
                }
            }

            // Generating the constraints and variables for each constraint tree
            foreach (var constraintTree in relevantConstraintTrees)
            {
                tmsConstraints.AddRange(constraintTree.GenerateTmsConstraints());
                tmsVariables.AddRange(constraintTree.GetNonUtilityDecisionVariables());
                tmsUtilityVariables.AddRange(constraintTree.GetUtilityDecisionVariables());
            }

            // Since some decision variables can be duplicated, we have to find the distinct ones
            // Additionally, for utility variables their utility factor has to be recalculated as needed
            tmsVariables        = TmsDecisionVariable.GetDistinctVariables(tmsVariables, false);
            tmsUtilityVariables = TmsDecisionVariable.GetDistinctVariables(tmsUtilityVariables, true);

            // Generating the composition constraints
            foreach (var nodeA in this.Nodes)
            {
                foreach (var nodeB in this.Nodes)
                {
                    foreach (var nodeC in this.Nodes)
                    {
                        QualitativeEdge edgeAB                = (QualitativeEdge)this.Edges[new Tuple <Node, Node>(nodeA, nodeB)];
                        QualitativeEdge edgeBC                = (QualitativeEdge)this.Edges[new Tuple <Node, Node>(nodeB, nodeC)];
                        QualitativeEdge edgeAC                = (QualitativeEdge)this.Edges[new Tuple <Node, Node>(nodeA, nodeC)];
                        string          edgeABDVar            = edgeAB.GetDVarName();
                        string          edgeBCDVar            = edgeBC.GetDVarName();
                        string          edgeACDVar            = edgeAC.GetDVarName();
                        TmsConstraint   compositionConstraint = new TmsConstraint();

                        // Creating the constraint restricting the allowed relations by the edge
                        compositionConstraint.ConstraintType = this.RelationFamily.GetTmsCompositionConstraintName();
                        compositionConstraint.VariableTuple  = new List <string>()
                        {
                            edgeABDVar, edgeBCDVar, edgeACDVar
                        };

                        tmsConstraints.Add(compositionConstraint);
                    }
                }
            }

            solutionInformation.Stopwatch.Stop();
            solutionInformation.Log.AddItem(LogType.Info, string.Format("TMS variables and constraints generated ({0} ms)", solutionInformation.Stopwatch.ElapsedMilliseconds));

            solutionInformation.Log.AddItem(LogType.Info, string.Format("TMS solution process started", solutionInformation.Stopwatch.ElapsedMilliseconds));
            solutionInformation.Log.AddItem(LogType.Info, string.Format("Variables (excl. soft): {0}", tmsVariables.Count));
            solutionInformation.Log.AddItem(LogType.Info, string.Format("Variables for soft constraints: {0}", tmsUtilityVariables.Count));
            solutionInformation.Log.AddItem(LogType.Info, string.Format("Constraints: {0}", tmsConstraints.Count));

            solutionInformation.Stopwatch.Restart();

            this.SolveWithTms(solutionInformation, tms, tmsVariables, tmsUtilityVariables, tmsConstraints);

            solutionInformation.Stopwatch.Stop();
            solutionInformation.Log.AddItem(LogType.Info, string.Format("TMS solution process complete ({0} ms)", solutionInformation.Stopwatch.ElapsedMilliseconds));
            //}
            //catch (Exception e)
            //{
            //    solutionInformation.Log.AddItem(LogType.Error, e.ToString());
            //}

            solutionInformation.SolutionFinished();

            return(solutionInformation);
        }