internal void Expand(Constraint violatedConstraint) { Debug_ClearDfDv(false /* forceFull */); // Calculate the derivative at the point of each constraint. // violatedConstraint's edge may be the minimum so pass null for variableDoneEval. // // We also want to find the path along the active constraint tree from violatedConstraint.Left // to violatedConstraint.Right, and find the constraint on that path with the lowest Langragian // multiplier. The ActiveConstraints form a spanning tree so there will be no more than // one path. violatedConstraint is not yet active so it will not appear in this list. if (null == this.constraintPath) { this.constraintPath = new List<ConstraintDirectionPair>(); } this.constraintPath.Clear(); this.pathTargetVariable = violatedConstraint.Right; #if VERBOSE Console.WriteLine("Before Block.Expand ComputeDfDv: {0}", this); #endif // VERBOSE ComputeDfDv(violatedConstraint.Left); #if VERBOSE Console.WriteLine("After Block.Expand ComputeDfDv: {0}", this); DumpState(null /* no prefix */); #endif // VERBOSE // Now find the forward non-equality constraint on the path that has the minimal Lagrangina. // Both variables of the constraint are in the same block so a path should always be found. Constraint minLagrangianConstraint = null; if (this.constraintPath.Count > 0) { // We found an existing path so must remove an edge from our active list so that all // connected variables from its varRight onward can move to the right; this will // make the "active" status false for that edge. The active non-Equality constraint // with the minimal Lagrangian *that points rightward* is our split point (do *not* // split Equality constraints). foreach (ConstraintDirectionPair pathItem in this.constraintPath) { #if VERBOSE Console.WriteLine("ConstraintPath: {0} ({1})", pathItem.Constraint, pathItem.IsForward ? "forward" : "backward"); #endif // VERBOSE if (pathItem.IsForward && ((null == minLagrangianConstraint) || (pathItem.Constraint.Lagrangian < minLagrangianConstraint.Lagrangian))) { if (!pathItem.Constraint.IsEquality) { minLagrangianConstraint = pathItem.Constraint; } } } #if VERBOSE DumpPath("Expand path", minLagrangianConstraint); #endif // VERBOSE if (null != minLagrangianConstraint) { // Deactivate this constraint as we are splitting on it. this.allConstraints.DeactivateConstraint(minLagrangianConstraint); } } this.constraintPath.Clear(); this.pathTargetVariable = null; if (null == minLagrangianConstraint) { // If no forward non-equality edge was found, violatedConstraint would have created a cycle. Debug.Assert(!violatedConstraint.IsUnsatisfiable, "An already-unsatisfiable constraint should not have been attempted"); violatedConstraint.IsUnsatisfiable = true; ++this.allConstraints.NumberOfUnsatisfiableConstraints; #if VERBOSE Console.WriteLine(" -- Expand: No forward non-equality edge (minLagrangianConstraint) found, therefore the constraint is unsatisfiable -- "); Console.WriteLine(" Unsatisfiable constraint: {0}", violatedConstraint); #endif // VERBOSE return; } // Note: for perf, expand in-place (as in Ipsep) rather than Split/Merge (as in the Scaling paper). // Adjust the offset of each variable at and past the right-hand side of violatedConstraint in the // active spanning tree. Because we've removed minLagrangianConstraint, this will widen the // gap between minLagrangianConstraint.Left and .Right. Note that this must include not only // violatedConstraint.Right and those to its right, but also those to its left that are connected // to it by active constraints - because the definition of an active constraint is that the // gap matches exactly with the actual position, so all will move as a unit. var lstConnectedVars = new List<Variable>(); // We consider .Left "already evaluated" because we don't want the path evaluation to back // up to it (because we're splitting .Right off from it by deactivating the constraint). GetConnectedVariables(lstConnectedVars, violatedConstraint.Right, violatedConstraint.Left); double violation = violatedConstraint.Violation; int cConnectedVars = lstConnectedVars.Count; for (int ii = 0; ii < cConnectedVars; ++ii) { lstConnectedVars[ii].OffsetInBlock += violation; } // Now make the (no-longer-) violated constraint active. this.allConstraints.ActivateConstraint(violatedConstraint); // Clear the DfDv values. For DEBUG, the new constraint came in from outside this block // so this will make sure it doesn't have a stale cycle-detection flag. violatedConstraint.ClearDfDv(); // Update this block's reference position. this.UpdateReferencePos(); #if VERBOSE Console.WriteLine("Block.Expand result: {0}", this); DumpState(null /* no prefix */); #endif // VERBOSE } // end Expand()