Example #1
0
 internal void RecycleDfDvNode(DfDvNode node)
 {
     // In the case of long constraint chains make sure this does not end up as big as the number of constraints in the block.
     if (this.DfDvRecycleStack.Count < 1024)
     {
         DfDvRecycleStack.Push(node);
     }
 }
        internal DfDvNode Set(DfDvNode parent, Constraint constraintToEval, Variable variableToEval, Variable variableDoneEval)
        {
            this.Parent = parent;
            this.ConstraintToEval = constraintToEval;
            this.VariableToEval = variableToEval;
            this.VariableDoneEval = variableDoneEval;
            this.Depth = 0;
            this.ChildrenHaveBeenPushed = false;

            constraintToEval.Lagrangian = 0.0;
            return this;
        }
Example #3
0
        internal DfDvNode Set(DfDvNode parent, Constraint constraintToEval, Variable variableToEval, Variable variableDoneEval)
        {
            this.Parent                 = parent;
            this.ConstraintToEval       = constraintToEval;
            this.VariableToEval         = variableToEval;
            this.VariableDoneEval       = variableDoneEval;
            this.Depth                  = 0;
            this.ChildrenHaveBeenPushed = false;

            constraintToEval.Lagrangian = 0.0;
            return(this);
        }
 internal void RecycleDfDvNode(DfDvNode node) {
     // In the case of long constraint chains make sure this does not end up as big as the number of constraints in the block.
     if (this.DfDvRecycleStack.Count < 1024) {
         DfDvRecycleStack.Push(node);
     }
 }
Example #5
0
 internal DfDvNode(DfDvNode parent, Constraint constraintToEval, Variable variableToEval, Variable variableDoneEval)
 {
     Set(parent, constraintToEval, variableToEval, variableDoneEval);
 }
 internal DfDvNode(DfDvNode parent, Constraint constraintToEval, Variable variableToEval, Variable variableDoneEval)
 {
     Set(parent, constraintToEval, variableToEval, variableDoneEval);
 }
 void CheckForConstraintPathTarget(DfDvNode node)
 {
     if (this.pathTargetVariable == node.VariableToEval)
     {
         // Add every variable from pathTargetVariable up the callchain up to but not including initialVarToEval.
         while (node.Parent != this.dfDvDummyParentNode)
         {
             this.constraintPath.Add(new ConstraintDirectionPair(node.ConstraintToEval, node.IsLeftToRight));
             node = node.Parent;
         }
         this.pathTargetVariable = null;         // Path is complete
     }
 }
        internal void RecurseGetConnectedVariables(List<Variable> lstVars, Variable initialVarToEval, Variable initialVarDoneEval)
        {
            // Get all the vars at and to the right of 'var', including backtracking to get all
            // variables that are connected from the left.  This is just like ComputeDfDv except
            // that in this case we start with the variableDoneEval being the Left variable.
            Debug.Assert(0 == this.allConstraints.DfDvStack.Count, "Leftovers in ComputeDfDvStack");
            this.allConstraints.DfDvStack.Clear();
            Debug.Assert(0 == lstVars.Count, "Leftovers in lstVars");

            // Variables for initializing the first node.
            var dummyConstraint = new Constraint(initialVarToEval);
            dfDvDummyParentNode = new DfDvNode(dummyConstraint);
            this.allConstraints.DfDvStack.Push(GetDfDvNode(dfDvDummyParentNode, dummyConstraint, initialVarToEval, initialVarDoneEval));
            lstVars.Add(initialVarToEval);

            // Do a pre-order tree traversal (process the constraint before its children), for consistency
            // with prior behaviour.
            while (this.allConstraints.DfDvStack.Count > 0)
            {
                // Leave the node on the stack until we've processed all of its children.
                var node = this.allConstraints.DfDvStack.Peek();
                int prevStackCount = this.allConstraints.DfDvStack.Count;

                if (!node.ChildrenHaveBeenPushed)
                {
                    node.ChildrenHaveBeenPushed = true;
                    foreach (var constraint in node.VariableToEval.LeftConstraints)
                    {
                        if (constraint.IsActive && (constraint.Right != node.VariableDoneEval))
                        {
                            // If the node has no constraints other than the one we're now processing, it's a leaf
                            // and we don't need the overhead of pushing to and popping from the stack.
                            if (1 == constraint.Right.ActiveConstraintCount)
                            {
                                Debug_CycleCheck(constraint);
                                Debug_MarkForCycleCheck(constraint);
                                lstVars.Add(constraint.Right);
                            }
                            else
                            {
                                // variableToEval is now considered "done"
                                AddVariableAndPushDfDvNode(lstVars, GetDfDvNode(node, constraint, constraint.Right, node.VariableToEval));
                            }
                        }
                    }

                    foreach (var constraint in node.VariableToEval.RightConstraints)
                    {
                        if (constraint.IsActive && (constraint.Left != node.VariableDoneEval))
                        {
                            // See comments in .LeftConstraints
                            if (1 == constraint.Left.ActiveConstraintCount)
                            {
                                Debug_CycleCheck(constraint);
                                Debug_MarkForCycleCheck(constraint);
                                lstVars.Add(constraint.Left);
                            }
                            else
                            {
                                AddVariableAndPushDfDvNode(lstVars, GetDfDvNode(node, constraint, constraint.Left, node.VariableToEval));
                            }
                        }
                    }
                } // endif !node.ChildrenHaveBeenPushed

                // If we just pushed one or more nodes, loop back up and "recurse" into them.
                if (this.allConstraints.DfDvStack.Count > prevStackCount)
                {
                    continue;
                }

                // We are at a non-leaf node and have "recursed" through all its descendents, so we're done with it.
                Debug.Assert(this.allConstraints.DfDvStack.Peek() == node, "DfDvStack.Peek() should be 'node'");
                this.allConstraints.RecycleDfDvNode(this.allConstraints.DfDvStack.Pop());
            } // endwhile stack is not empty
        }
 // Called by RecurseGetConnectedVariables.
 void AddVariableAndPushDfDvNode(List<Variable> lstVars, DfDvNode node)
 {
     Debug_CycleCheck(node.ConstraintToEval);
     lstVars.Add(node.VariableToEval);
     PushOnDfDvStack(node);
 }
 void PushOnDfDvStack(DfDvNode node)
 {
     Debug_MarkForCycleCheck(node.ConstraintToEval);
     this.allConstraints.DfDvStack.Push(node);
 }
        // Called by ComputeDfDv.
        void PushDfDvNode(DfDvNode node)
        {
#if VERBOSE
            Console.WriteLine("ComputeDfDv depth {0} pushing non-leaf {1}Constraint: {2}",
                    node.Depth, node.IsLeftToRight ? "Left" : "Right", node.ConstraintToEval);
#endif // VERBOSE
            Debug_CycleCheck(node.ConstraintToEval);
            PushOnDfDvStack(node);
        }
 DfDvNode GetDfDvNode(DfDvNode parent, Constraint constraintToEval, Variable variableToEval, Variable variableDoneEval)
 {
     DfDvNode node = (this.allConstraints.DfDvRecycleStack.Count > 0)
                     ? this.allConstraints.DfDvRecycleStack.Pop().Set(parent, constraintToEval, variableToEval, variableDoneEval)
                     : new DfDvNode(parent, constraintToEval, variableToEval, variableDoneEval);
     node.Depth = node.Parent.Depth + 1;
     if (this.allConstraints.MaxConstraintTreeDepth < node.Depth)
     {
         this.allConstraints.MaxConstraintTreeDepth = node.Depth;
     }
     return node;
 }
        // Directly evaluate a leaf node rather than defer it to stack push/pop.
        void ProcessDfDvLeafNodeDirectly(DfDvNode node)
        {
#if VERBOSE
            Console.WriteLine("ComputeDfDv directly processing the following leaf constraint:");
#endif // VERBOSE
            Debug_MarkForCycleCheck(node.ConstraintToEval);
            ProcessDfDvLeafNode(node);
        }
        } // end ComputeDfDv()

        void ProcessDfDvLeafNode(DfDvNode node)
        {
#if VERBOSE
            Console.WriteLine("  ComputeDfDv depth {0} evaluating {1}Constraint: {2}", 
                                node.Depth, node.IsLeftToRight ? "Left" : "Right", node.ConstraintToEval);

            // If pathTargetVariable is non-null, we haven't found it yet, so this constraint is a dead end.
            if (null != this.pathTargetVariable) {
                Console.WriteLine("    {0} dead end: {1}", node.IsLeftToRight ? "LtoR" : "RtoL", node.ConstraintToEval);
            }
#endif // VERBOSE

            double dfdv = node.VariableToEval.DfDv;

            // Add dfdv to constraint.Lagrangian if we are going left-to-right, else subtract it ("negative slope");
            // similarly, add it to or subtract it from the parent's Lagrangian.
            if (node.IsLeftToRight)
            {
                node.ConstraintToEval.Lagrangian += dfdv;
                node.Parent.ConstraintToEval.Lagrangian += node.ConstraintToEval.Lagrangian;
            }
            else
            {
                // Any child constraints have already put their values into the current constraint 
                // according to whether they were left-to-right or right-to-left.  This is the equivalent
                // to the sum of return values in the recursive approach in the paper.  However, the paper
                // negates this return value when setting it into a right-to-left parent's Lagrangian;
                // we're that right-to-left parent now so do that first (negate the sum of children).
                node.ConstraintToEval.Lagrangian = -(node.ConstraintToEval.Lagrangian + dfdv);
                node.Parent.ConstraintToEval.Lagrangian -= node.ConstraintToEval.Lagrangian;
            }
#if VERBOSE
            Console.WriteLine("  ComputeDfDv: incremental {0} dfdv = {1:F5}", node.IsLeftToRight ? "LToR" : "RToL", dfdv);
            Console.WriteLine("    Constraint {0} Lagrangian: {1:F5} (scale when {2} parent = {3:F5})",
                                node.ConstraintToEval.Id, node.ConstraintToEval.Lagrangian,
                                node.IsLeftToRight ? "added to" : "subtracted from",
                                node.IsLeftToRight ? node.ConstraintToEval.Left.Scale : node.ConstraintToEval.Right.Scale);
            Console.WriteLine("        Parent {0} Lagrangian: {1:F5}",
                                node.Parent.ConstraintToEval.Id, node.Parent.ConstraintToEval.Lagrangian);
#endif // VERBOSE

            // See if this node found the target variable.
            CheckForConstraintPathTarget(node);

            // If this active constraint is violated, record it.
            Debug_CheckForViolatedActiveConstraint(node.ConstraintToEval);

            // We're done with this node.
            this.allConstraints.RecycleDfDvNode(node);
        }
        internal void ComputeDfDv(Variable initialVarToEval)
        {
#if COMPARE_RECURSIVE_DFDV
            var recursiveDfDv = Recursive_DfDv(initialVarToEval, null, 0);
#endif // COMPARE_RECURSIVE_DFDV
#if DEBUG
            Debug.Assert(0 != this.idDfDv, "idDfDv should not be 0");
#endif // DEBUG
#if VERBOSE
            Console.WriteLine("ComputeDfDv initialVarToEval: [{0}]", initialVarToEval);
#endif // VERBOSE

            // Compute the derivative of the spanning tree (comprised of our active constraints) at the
            // point of variableToEval (with "change" being the difference between "Desired" position and the calculated
            // position for the current pass), for all paths that do not include the edge variableToEval->variableDoneEval.

            // Recursiteratively process all outgoing paths from variableToEval to its right (i.e. where it is constraint.Left),
            // but don't include variableDoneEval because it's already been evaluated.
            // At each variable on the rightward traversal, we'll also process leftward paths (incoming to) that
            // variable (via the following constraint loop) before returning here.
            // variableToEval and variableDoneEval (if not null) are guaranteed to be in this Block, since they're co-located
            // in an active Constraint in this Block.
            //
            // For Expand, we want to find the constraint path from violatedConstraint.Left to violatedConstraint.Right;
            // the latter is in pathTargetVariable.  This is ComputePath from the doc.  The logic there is:
            //    Do the iterations of ComputeDvDv
            //    If we find the target, then traverse the parent chain to populate the list bottom-up

            Debug.Assert(0 == this.allConstraints.DfDvStack.Count, "Leftovers in ComputeDfDvStack");
            this.allConstraints.DfDvStack.Clear();

            // Variables for initializing the first node.
            var dummyConstraint = new Constraint(initialVarToEval);
            dfDvDummyParentNode = new DfDvNode(dummyConstraint);
            var firstNode = GetDfDvNode(dfDvDummyParentNode, dummyConstraint, initialVarToEval, null /*no "done" var yet*/);
            this.allConstraints.DfDvStack.Push(firstNode);

            // Iteratively recurse, processing all children of a constraint before the constraint itself.
            // Loop termination is by testing for completion based on node==firstNode which is faster than 
            // (non-inlined) Stack.Count.
            for (; ; )
            {
                // Leave the node on the stack until we've processed all of its children.
                var node = this.allConstraints.DfDvStack.Peek();
                int prevStackCount = this.allConstraints.DfDvStack.Count;

#if VERBOSE
                Console.WriteLine("ComputeDfDv peeking at node {0}: varDoneEval {1}, {2}Constraint: {3}",
                                    node, node.VariableDoneEval, node.IsLeftToRight ? "Left" : "Right", node.ConstraintToEval);
#endif // VERBOSE

                if (!node.ChildrenHaveBeenPushed)
                {
                    node.ChildrenHaveBeenPushed = true;
                    foreach (var constraint in node.VariableToEval.LeftConstraints)
                    {
                        // Direct violations (a -> b -> a) are not caught by the constraint-based cycle detection
                        // because VariableDoneEval prevents them from being entered (b -> a is not entered because a is
                        // VariableDoneEval).  These cycles should be caught by the null-minLagrangian IsUnsatisfiable
                        // setting in Block.Expand (but assert with IsActive not IsUnsatisfiable, as the constraint
                        // may not have been encountered yet).  Test_Unsatisfiable_Cycle_InDirect_With_SingleConstraint_Var.
                        Debug.Assert(!constraint.IsActive || !(node.IsLeftToRight && (constraint.Right == node.VariableDoneEval)),
                                "this cycle should not happen");
                        if (constraint.IsActive && (constraint.Right != node.VariableDoneEval))
                        {
                            // variableToEval is now considered "done"
                            var childNode = GetDfDvNode(node, constraint, constraint.Right, node.VariableToEval);

                            // If the node has no constraints other than the one we're now processing, it's a leaf
                            // and we don't need the overhead of pushing to and popping from the stack.
                            if (1 == constraint.Right.ActiveConstraintCount)
                            {
                                ProcessDfDvLeafNodeDirectly(childNode);
                            }
                            else
                            {
                                PushDfDvNode(childNode);
                            }
                        }
                    }

                    foreach (var constraint in node.VariableToEval.RightConstraints)
                    {
                        // See comments in .LeftConstraints.
                        Debug.Assert(!constraint.IsActive || !(!node.IsLeftToRight && (constraint.Left == node.VariableDoneEval)),
                                "this cycle should not happen");
                        if (constraint.IsActive && (constraint.Left != node.VariableDoneEval))
                        {
                            var childNode = GetDfDvNode(node, constraint, constraint.Left, node.VariableToEval);
                            if (1 == constraint.Left.ActiveConstraintCount)
                            {
                                ProcessDfDvLeafNodeDirectly(childNode);
                            }
                            else
                            {
                                PushDfDvNode(childNode);
                            }
                        }
                    }

                    // If we just pushed one or more nodes, loop back up and "recurse" into them.
                    if (this.allConstraints.DfDvStack.Count > prevStackCount)
                    {
                        continue;
                    }
                } // endif !node.ChildrenHaveBeenPushed

                // We are at a non-leaf node and have "recursed" through all its descendents; therefore pop it off
                // the stack and process it.  If it's the initial node, we've already updated DummyConstraint.Lagrangian
                // from all child nodes, and it's in the DummyParentNode as well so this will add the final dfdv.
#if VERBOSE
                Console.WriteLine("ComputeDfDv: Node has no children or all have been processed");
#endif // VERBOSE
                Debug.Assert(this.allConstraints.DfDvStack.Peek() == node, "DfDvStack.Peek() should be 'node'");
                this.allConstraints.DfDvStack.Pop();
                ProcessDfDvLeafNode(node);
                if (node == firstNode)
                {
                    Debug.Assert(0 == this.allConstraints.DfDvStack.Count, "Leftovers in DfDvStack on completion of loop");
                    break;
                }
            } // endwhile stack is not empty

#if VERBOSE
            Console.WriteLine("ComputeDfDv result: {0:F5}", dummyConstraint.Lagrangian);
#endif // VERBOSE

#if DEBUG
            // From the definition of the optimal position of all variables that satisfies the constraints, the
            // final value of this should be zero.  Think of the constraints as rigid rods and the variables as
            // the attachment points of the rods.  Also think of those attachment points as having springs connecting
            // them to their ideal positions.  In order for the whole system to be at rest (i.e. at the optimal
            // position) the net force on the right-hand side of each rod must be equal and opposite to the net
            // force on the left-hand side.  Thus, the final return value of compute_dfdv is the sum of the entire
            // left-hand side and right-hand side - which should cancel (within rounding error).
            ////
            // The dummyConstraint "rolls up" to its parent which is itself, thus it will be twice the leftover.
            DebugVerifyFinalDfDvValue(dummyConstraint.Lagrangian / 2.0,
                    String.Format(CultureInfo.InvariantCulture, "nonzero final ComputeDfDv value ({0})", dummyConstraint.Lagrangian));
#if COMPARE_RECURSIVE_DFDV
            DebugVerifyFinalDfDvValue((dummyConstraint.Lagrangian / 2.0) - recursiveDfDv,
                    String.Format(CultureInfo.InvariantCulture, "Unequal DfDv values; Recursive = {0}, iterative = {1}", recursiveDfDv, dummyConstraint.Lagrangian));
#endif // COMPARE_RECURSIVE_DFDV
#endif // DEBUG
        } // end ComputeDfDv()