Example #1
0
        /// <summary>
        /// Create lists on integers within each option that indicates what other
        /// options in that list it is confluent with. As discussed below confluence
        /// is commutative which saves a little time in this function, but it is not
        /// transitive - meaning is A is confluent with B and C. It is not necessarily
        /// true that B is confluent with C.
        /// </summary>
        /// <param name="options">The list of options to assign confluence</param>
        /// <param name="cand">The cand.</param>
        /// <param name="confluenceAnalysis">The confluence analysis.</param>
        /// <returns></returns>
        public static int[,] AssignOptionConfluence(List <option> options, candidate cand,
                                                    ConfluenceAnalysis confluenceAnalysis)
        {
            var numOpts            = options.Count;
            var invalidationMatrix = MakeInvalidationMatrix(options, cand, confluenceAnalysis);

            foreach (var o in options)
            {
                o.confluence = new List <int>();
            }
            for (var i = 0; i < numOpts - 1; i++)
            {
                for (var j = i + 1; j < numOpts; j++)
                {
                    if (confluenceAnalysis == ConfluenceAnalysis.OptimisticSimple)
                    {
                        if (invalidationMatrix[i, j] <= 0 && invalidationMatrix[j, i] <= 0)
                        {
                            options[i].confluence.Add(j);
                            options[j].confluence.Add(i);
                        }
                    }
                    // else confluenceAnalysis == ConfluenceAnalysis.ConservativeSimple
                    else if (invalidationMatrix[i, j] < 0 && invalidationMatrix[j, i] < 0)
                    {
                        options[i].confluence.Add(j);
                        options[j].confluence.Add(i);
                    }
                }
            }

            return(invalidationMatrix);
        }
Example #2
0
        /// <summary>
        /// Predicts whether the option p  invalidates option q.
        /// This invalidation is a tricky thing. For the most part, this function
        /// has been carefully coded to handle all cases. The only exceptions
        /// are from what the additional recognize and apply functions require or modify.
        /// This is handled by actually testing to see if this is true.
        /// </summary>
        /// <param name="p">The p.</param>
        /// <param name="q">The q.</param>
        /// <param name="cand">The cand.</param>
        /// <param name="confluenceAnalysis">The confluence analysis.</param>
        /// <returns></returns>
        private static int doesPInvalidateQ(option p, option q, candidate cand, ConfluenceAnalysis confluenceAnalysis)
        {
            #region Global Labels
            var pIntersectLabels = p.rule.L.globalLabels.Intersect(p.rule.R.globalLabels);
            var pRemovedLabels   = new List <string>(p.rule.L.globalLabels);
            pRemovedLabels.RemoveAll(s => pIntersectLabels.Contains(s));
            var pAddedLabels = new List <string>(p.rule.R.globalLabels);
            pAddedLabels.RemoveAll(s => pIntersectLabels.Contains(s));

            if ( /* first check that there are no labels deleted that the other depeonds on*/
                (q.rule.L.globalLabels.Intersect(pRemovedLabels).Any()) ||

                /* adding labels is problematic if the other rule was recognized under
                 * the condition of containsAllLocalLabels. */
                ((q.rule.containsAllGlobalLabels) && (pAddedLabels.Any())) ||

                /* adding labels is also problematic if you add a label that negates the
                 * other rule. */
                (pAddedLabels.Intersect(q.rule.negateLabels).Any()))
            {
                return(1);
            }
            #endregion

            #region Nodes

            /* first we check the nodes. If two options do not share any nodes, then
             * the whole block of code is skipped. q is to save time if comparing many
             * options on a large graph. However, since there is some need to understand what
             * nodes are saved in rule execution, the following two lists are defined outside
             * of q condition and are used in the Arcs section below. */
            /* why are the following three parameters declared here and not in scope with the
             * other node parameters below? This is because they are used in the induced and
             * shape restriction calculations below - why calculate twice? */
            int      Num_pKNodes  = 0;
            string[] pNodesKNames = null;
            node[]   pKNodes      = null;

            var commonNodes = q.nodes.Intersect(p.nodes);
            if (commonNodes.Any())
            /* if there are no common nodes, then no need to check the details. */
            {
                /* the following arrays of nodes are within the rule not the host. */

                #region  Check whether there are nodes that p will delete that q depends upon.
                var pNodesLNames = from n in p.rule.L.nodes
                                   where ((ruleNode)n).MustExist
                                   select n.name;
                var pNodesRNames = from n in p.rule.R.nodes
                                   select n.name;
                pNodesKNames = pNodesRNames.Intersect(pNodesLNames).ToArray();
                Num_pKNodes  = pNodesKNames.GetLength(0);
                pKNodes      = new node[Num_pKNodes];
                for (var i = 0; i < p.rule.L.nodes.Count; i++)
                {
                    var index = Array.IndexOf(pNodesKNames, p.rule.L.nodes[i].name);
                    if (index >= 0)
                    {
                        pKNodes[index] = p.nodes[i];
                    }
                    else if (commonNodes.Contains(p.nodes[i]))
                    {
                        return(1);
                    }
                }
                #endregion

                #region NodesModified

                /* in the above regions where deletions are checked, we also create lists for potentially
                 * modified nodes, nodes common to both L and R. We will now check these. There are several
                 * ways that a node can be modified:
                 * 1. labels are removed
                 * 2. labels are added (and potentially in the negabels of the other rule).
                 * 3. number of arcs connected, which affect strictDegreeMatch
                 * 4. variables are added/removed/changed
                 *
                 * There first 3 conditions are check all at once below. For the last one, it is impossible
                 * to tell without executing extra functions that the user may have created for rule
                 * recognition. Therefore, additional functions are not check in q confluence check. */
                foreach (var commonNode in commonNodes)
                {
                    var qNodeL = (ruleNode)q.rule.L.nodes[q.nodes.IndexOf(commonNode)];
                    var pNodeL = (ruleNode)p.rule.L.nodes[p.nodes.IndexOf(commonNode)];
                    var pNodeR = (ruleNode)p.rule.R[pNodeL.name];
                    pIntersectLabels = pNodeL.localLabels.Intersect(pNodeR.localLabels);
                    pRemovedLabels   = new List <string>(pNodeL.localLabels);
                    pRemovedLabels.RemoveAll(s => pIntersectLabels.Contains(s));
                    pAddedLabels = new List <string>(pNodeR.localLabels);
                    pAddedLabels.RemoveAll(s => pIntersectLabels.Contains(s));
                    if ( /* first check that there are no labels deleted that the other depeonds on*/
                        (qNodeL.localLabels.Intersect(pRemovedLabels).Any()) ||

                        /* adding labels is problematic if the other rule was recognized under
                         * the condition of containsAllLocalLabels. */
                        ((qNodeL.containsAllLocalLabels) && (pAddedLabels.Any())) ||

                        /* adding labels is also problematic if you add a label that negates the
                         * other rule. */
                        (pAddedLabels.Intersect(qNodeL.negateLabels).Any()) ||

                        /* if one rule uses strictDegreeMatch, we need to make sure the other rule
                         * doesn't change the degree. */
                        (qNodeL.strictDegreeMatch && (pNodeL.degree != pNodeR.degree)) ||

                        /* actually, the degree can also change from free-arc embedding rules. These
                         * are checked below. */
                        (qNodeL.strictDegreeMatch &&
                         (p.rule.embeddingRules.FindAll(e => (e.RNodeName.Equals(pNodeR.name))).Count > 0)))
                    {
                        return(1);
                    }
                }
                #endregion
            }

            #endregion
            #region Arcs
            var commonArcs = q.arcs.Intersect(p.arcs);
            if (commonArcs.Any())
            /* if there are no common arcs, then no need to check the details. */
            {
                /* the following arrays of arcs are within the rule not the host. */
                #region  Check whether there are arcs that p will delete that q depends upon.
                var pArcsLNames = from n in p.rule.L.arcs
                                  where ((ruleArc)n).MustExist
                                  select n.name;
                var pArcsRNames = from n in p.rule.R.arcs
                                  select n.name;
                var pArcsKNames = new List <string>(pArcsRNames.Intersect(pArcsLNames));
                var pKArcs      = new arc[pArcsKNames.Count()];
                for (var i = 0; i < p.rule.L.arcs.Count; i++)
                {
                    if (pArcsKNames.Contains(p.rule.L.arcs[i].name))
                    {
                        pKArcs[pArcsKNames.IndexOf(p.rule.L.arcs[i].name)] = p.arcs[i];
                    }
                    else if (commonArcs.Contains(p.arcs[i]))
                    {
                        return(1);
                    }
                }
                #endregion


                #region ArcsModified
                foreach (var commonArc in commonArcs)
                {
                    var qArcL = (ruleArc)q.rule.L.arcs[q.arcs.IndexOf(commonArc)];
                    var pArcL = (ruleArc)p.rule.L.arcs[p.arcs.IndexOf(commonArc)];
                    var pArcR = (ruleArc)p.rule.R[pArcL.name];
                    pIntersectLabels = pArcL.localLabels.Intersect(pArcR.localLabels);
                    pRemovedLabels   = new List <string>(pArcL.localLabels);
                    pRemovedLabels.RemoveAll(s => pIntersectLabels.Contains(s));
                    pAddedLabels = new List <string>(pArcR.localLabels);
                    pAddedLabels.RemoveAll(s => pIntersectLabels.Contains(s));
                    if ( /* first check that there are no labels deleted that the other depeonds on*/
                        (qArcL.localLabels.Intersect(pRemovedLabels).Any()) ||

                        /* adding labels is problematic if the other rule was recognized under
                         * the condition of containsAllLocalLabels. */
                        ((qArcL.containsAllLocalLabels) && (pAddedLabels.Any())) ||

                        /* adding labels is also problematic if you add a label that negates the
                         * other rule. */
                        (pAddedLabels.Intersect(qArcL.negateLabels).Any()) ||

                        /* if one rule uses strictDegreeMatch, we need to make sure the other rule
                         * doesn't change the degree. */

                        /* if one rule requires that an arc be dangling for correct recognition (nullMeansNull)
                         * then we check to make sure that the other rule doesn't add a node to it. */
                        ((qArcL.nullMeansNull) &&
                         (((qArcL.From == null) && (pArcR.From != null)) ||
                          ((qArcL.To == null) && (pArcR.To != null)))) ||

                        /* well, even if the dangling isn't required, we still need to ensure that p
                         * doesn't put a node on an empty end that q is expecting to belong
                         * to some other node. */
                        ((pArcL.From == null) && (pArcR.From != null) && (qArcL.From != null)) ||
                        /* check the To direction as well */
                        ((pArcL.To == null) && (pArcR.To != null) && (qArcL.To != null)) ||

                        /* in q, the rule is not matching with a dangling arc, but we need to ensure that
                         * the rule doesn't remove or re-connect the arc to something else in the K of the rule
                         * such that the recogniation of the second rule is invalidated. q may be a tad strong
                         * (or conservative) as there could still be confluence despite the change in connectivity.
                         * How? I have yet to imagine. But clearly the assumption here is that change in
                         * connectivity prevent confluence. */
                        ((pArcL.From != null) &&
                         (pNodesKNames != null && pNodesKNames.Contains(pArcL.From.name)) &&
                         ((pArcR.From == null) || (pArcL.From.name != pArcR.From.name))) ||
                        ((pArcL.To != null) &&
                         (pNodesKNames != null && pNodesKNames.Contains(pArcL.To.name)) &&
                         ((pArcR.To == null) || (pArcL.To.name != pArcR.To.name))) ||

                        /* Changes in Arc Direction
                         *
                         * /* finally we check that the changes in arc directionality (e.g. making
                         * directed, making doubly-directed, making undirected) do not affect
                         * the other rule. */
                        /* Here, the directionIsEqual restriction is easier to check than the
                         * default case where directed match with doubly-directed and undirected
                         * match with directed. */
                        ((qArcL.directionIsEqual) &&
                         ((!qArcL.directed.Equals(pArcR.directed)) ||
                          (!qArcL.doublyDirected.Equals(pArcR.doublyDirected)))) ||
                        ((qArcL.directed && !pArcR.directed) ||
                         (qArcL.doublyDirected && !pArcR.doublyDirected))
                        )
                    {
                        return(1);
                    }
                }
                #endregion
            }
            #endregion

            #region HyperArcs
            /* Onto hyperarcs! q is similiar to nodes - more so than arcs. */
            var commonHyperArcs = q.hyperarcs.Intersect(p.hyperarcs);
            if (commonHyperArcs.Any())
            {
                #region  Check whether there are hyperarcs that p will delete that q.option depends upon.

                var pHyperArcsLNames = from n in p.rule.L.hyperarcs
                                       where ((ruleHyperarc)n).MustExist
                                       select n.name;
                var pHyperArcsRNames = from n in p.rule.R.hyperarcs select n.name;
                var pHyperArcsKNames = new List <string>(pHyperArcsRNames.Intersect(pHyperArcsLNames));
                var pKHyperarcs      = new hyperarc[pHyperArcsKNames.Count()];

                for (var i = 0; i < p.rule.L.hyperarcs.Count; i++)
                {
                    if (pHyperArcsKNames.Contains(p.rule.L.hyperarcs[i].name))
                    {
                        pKHyperarcs[pHyperArcsKNames.IndexOf(p.rule.L.hyperarcs[i].name)] = p.hyperarcs[i];
                    }
                    else if (commonHyperArcs.Contains(p.hyperarcs[i]))
                    {
                        return(1);
                    }
                }
                #endregion

                #region HyperArcsModified
                foreach (var commonHyperArc in commonHyperArcs)
                {
                    var qHyperArcL = (ruleHyperarc)q.rule.L.hyperarcs[q.hyperarcs.IndexOf(commonHyperArc)];
                    var pHyperArcL = (ruleHyperarc)p.rule.L.hyperarcs[p.hyperarcs.IndexOf(commonHyperArc)];
                    var pHyperArcR = (ruleHyperarc)p.rule.R[pHyperArcL.name];
                    pIntersectLabels = pHyperArcL.localLabels.Intersect(pHyperArcR.localLabels);
                    pRemovedLabels   = new List <string>(pHyperArcL.localLabels);
                    pRemovedLabels.RemoveAll(s => pIntersectLabels.Contains(s));
                    pAddedLabels = new List <string>(pHyperArcR.localLabels);
                    pAddedLabels.RemoveAll(s => pIntersectLabels.Contains(s));

                    if ( /* first check that there are no labels deleted that the other depends on*/
                        (qHyperArcL.localLabels.Intersect(pRemovedLabels).Any()) ||

                        /* adding labels is problematic if the other rule was recognized under
                         * the condition of containsAllLocalLabels. */
                        ((qHyperArcL.containsAllLocalLabels) && (pAddedLabels.Any())) ||

                        /* adding labels is also problematic if you add a label that negates the
                         * other rule. */
                        (pAddedLabels.Intersect(qHyperArcL.negateLabels).Any()) ||

                        /* if one rule uses strictDegreeMatch, we need to make sure the other rule
                         * doesn't change the degree. */
                        (qHyperArcL.strictNodeCountMatch && (pHyperArcL.degree != pHyperArcR.degree)))
                    {
                        /* actually, the degree can also change from free-arc embedding rules. These
                         * are checked below. */
                        return(1);
                    }
                }
                #endregion
            }
            #endregion

            #region now we're left with some tricky checks...
            if (commonNodes.Any())
            {
                #region if q is induced

                /* if q is induced then p will invalidate it, if it adds arcs between the
                 * common nodes. */
                if (q.rule.induced)
                {
                    var pArcsLNames = from a in p.rule.L.arcs select a.name;
                    if ((from newArc in p.rule.R.arcs.Where(a => !pArcsLNames.Contains(a.name))
                         where newArc.To != null && newArc.From != null
                         let toName = newArc.To.name
                                      let fromName = newArc.To.name
                                                     where pNodesKNames.Contains(toName) && pNodesKNames.Contains(fromName)
                                                     where commonNodes.Contains(pKNodes[Array.IndexOf(pNodesKNames, toName)]) &&
                                                     commonNodes.Contains(pKNodes[Array.IndexOf(pNodesKNames, fromName)])
                                                     select toName).Any())
                    {
                        return(1);
                    }

                    /* is there another situation in which an embedding rule in p may work against
                     * q being an induced rule? It doesn't seem like it would seem embedding rules
                     * reattach free-arcs. oh, what about arc duplication in embedding rules? nah. */
                }

                #endregion

                #region shape restrictions
                for (int i = 0; i < Num_pKNodes; i++)
                {
                    var pNode = pKNodes[i];
                    if (commonNodes.Contains(pNode))
                    {
                        continue;
                    }
                    var pname = pNodesKNames[i];
                    var lNode = (node)p.rule.L[pname];
                    var rNode = (node)p.rule.R[pname];

                    if (q.rule.UseShapeRestrictions && p.rule.TransformNodePositions &&
                        !(MatrixMath.sameCloseZero(lNode.X, rNode.X) &&
                          MatrixMath.sameCloseZero(lNode.Y, rNode.Y) &&
                          MatrixMath.sameCloseZero(lNode.Z, rNode.Z)))
                    {
                        return(1);
                    }
                    if ((q.rule.RestrictToNodeShapeMatch && p.rule.TransformNodeShapes && lNode.DisplayShape != null &&
                         rNode.DisplayShape != null) &&
                        !(MatrixMath.sameCloseZero(lNode.DisplayShape.Height, rNode.DisplayShape.Height) &&
                          MatrixMath.sameCloseZero(lNode.DisplayShape.Width, rNode.DisplayShape.Width) &&
                          MatrixMath.sameCloseZero(p.positionTransform[0, 0], 1) &&
                          MatrixMath.sameCloseZero(p.positionTransform[1, 1], 1) &&
                          MatrixMath.sameCloseZero(p.positionTransform[1, 0]) &&
                          MatrixMath.sameCloseZero(p.positionTransform[0, 1])))
                    {
                        return(1);
                    }
                }
                #endregion
            }

            /* you've run the gauntlet of easy checks, now need to check
             * (1) if there is something caught by additional recognition functions,
             * or (2) NOTExist elements now exist. These can only be solving by an empirical
             * test, which will be expensive.
             * So, now we switch from conditions that return true (or a 1; p does invalidate q) to conditions that return false.
             */

            if (q.rule.ContainsNegativeElements || q.rule.recognizeFuncs.Any() || p.rule.applyFuncs.Any())
            {
                if (confluenceAnalysis == ConfluenceAnalysis.Full)
                {
                    return(fullInvalidationCheck(p, q, cand));
                }
                else
                {
                    return(0);   //return 0 is like "I don't know"
                }
            }
            return(-1);   //like false, there is no invalidating - in otherwords confluence (maybe)!

            #endregion
        }
Example #3
0
        /// <summary>
        /// Makes the invalidation matrix.
        /// a -1 means that the row option does NOT violate the column option
        /// a 0 means Maybe - this happens when the ConfluenceAnalysis is not set to Full and rules have additional functions
        /// a +1 means that the row option DOES invalide the column option
        /// </summary>
        /// <param name="options">The options.</param>
        /// <param name="cand">The cand.</param>
        /// <param name="confluenceAnalysis">The confluence analysis.</param>
        /// <returns></returns>
        public static int[,] MakeInvalidationMatrix(List <option> options, candidate cand, ConfluenceAnalysis confluenceAnalysis)
        {
            var numOpts            = options.Count;
            var invalidationMatrix = new int[numOpts, numOpts];

            for (var i = 0; i < numOpts; i++)
            {
                for (var j = 0; j < numOpts; j++)
                {
                    if (i == j)
                    {
                        invalidationMatrix[i, j] = -1;
                    }
                    else
                    {
                        invalidationMatrix[i, j] = doesPInvalidateQ(options[i], options[j], cand, confluenceAnalysis);
                    }
                }
            }
            return(invalidationMatrix);
        }