/// <summary> /// Adds to "lines" the lines implied by initial-variable columns not in basis /// (see section 3.4.2 of Cousot and Halbwachs), and adds to "constraints" the /// constraints to exclude those lines (see step 4.2 of section 3.4.3 of /// Cousot and Halbwachs). /// </summary> /// <param name="lines"></param> /// <param name="constraints"></param> public void ProduceLines(ArrayList /*FrameElement*//*!*/ lines, ArrayList /*LinearConstraint*//*!*/ constraints) { Contract.Requires(constraints != null); Contract.Requires(lines != null); // for every initial variable not in basis for (int i0 = 0; i0 < numInitialVars; i0++) { if (inBasis[i0] == -1) { FrameElement fe = new FrameElement(); LinearConstraint lc = new LinearConstraint(LinearConstraint.ConstraintRelation.EQ); for (int i = 0; i < numInitialVars; i++) { if (i == i0) { fe.AddCoordinate((IVariable)cce.NonNull(dims[i]), Rational.ONE); lc.SetCoefficient((IVariable)cce.NonNull(dims[i]), Rational.ONE); } else if (inBasis[i] != -1) { // i is a basis column Contract.Assert(m[inBasis[i], i].HasValue(1)); Rational val = -m[inBasis[i], i0]; fe.AddCoordinate((IVariable)cce.NonNull(dims[i]), val); lc.SetCoefficient((IVariable)cce.NonNull(dims[i]), val); } } lines.Add(fe); constraints.Add(lc); } } }
public FrameElement Clone() { FrameElement z = new FrameElement(); z.terms = (Hashtable /*IVariable->Rational*/)this.terms.Clone(); return(z); }
/// <summary> /// Determines whether or not a given vertex or ray saturates the constraint. /// </summary> /// <param name="fe"></param> /// <param name="vertex">true if "fe" is a vertex; false if "fe" is a ray</param> /// <returns></returns> public bool IsSaturatedBy(FrameElement /*!*/ fe, bool vertex) { Contract.Requires(fe != null); Rational lhs = EvaluateLhs(fe); Rational rhs = vertex ? this.rhs : Rational.ZERO; return(lhs == rhs); }
/// <summary> /// Returns the left-hand side of the constraint evaluated at the point "v". /// Any coordinate not present in "v" is treated as if it were 0. /// Stated differently, this routine treats the left-hand side of the constraint /// as a row vector and "v" as a column vector, and then returns the dot-product /// of the two. /// </summary> /// <param name="v"></param> /// <returns></returns> public Rational EvaluateLhs(FrameElement /*!*/ v) { Contract.Requires(v != null); Rational q = Rational.ZERO; foreach (DictionaryEntry /*IVariable,Rational*/ term in coefficients) { IVariable dim = (IVariable /*!*/)cce.NonNull(term.Key); Rational a = (Rational)(cce.NonNull(term.Value)); Rational x = v[dim]; q += a * x; } return(q); }
public FrameElement Rename(IVariable /*!*/ oldName, IVariable /*!*/ newName) { Contract.Requires(newName != null); Contract.Requires(oldName != null); object /*Rational*/ z = terms[oldName]; if (z == null) { return(this); } else { System.Diagnostics.Debug.Assert(z is Rational); Hashtable /*IVariable->Rational*/ newTerms = (Hashtable /*!*/ /*IVariable->Rational*/)cce.NonNull(terms.Clone()); newTerms.Remove(oldName); newTerms.Add(newName, z); FrameElement fe = new FrameElement(); fe.terms = newTerms; return(fe); } }
/// <summary> /// Determines whether or not a given vertex or ray saturates the constraint. /// </summary> /// <param name="fe"></param> /// <param name="vertex">true if "fe" is a vertex; false if "fe" is a ray</param> /// <returns></returns> public bool IsSaturatedBy(FrameElement/*!*/ fe, bool vertex) { Contract.Requires(fe != null); Rational lhs = EvaluateLhs(fe); Rational rhs = vertex ? this.rhs : Rational.ZERO; return lhs == rhs; }
/// <summary> /// Returns the left-hand side of the constraint evaluated at the point "v". /// Any coordinate not present in "v" is treated as if it were 0. /// Stated differently, this routine treats the left-hand side of the constraint /// as a row vector and "v" as a column vector, and then returns the dot-product /// of the two. /// </summary> /// <param name="v"></param> /// <returns></returns> public Rational EvaluateLhs(FrameElement/*!*/ v) { Contract.Requires(v != null); Rational q = Rational.ZERO; foreach (DictionaryEntry /*IVariable,Rational*/ term in coefficients) { IVariable dim = (IVariable/*!*/)cce.NonNull(term.Key); Rational a = (Rational)(cce.NonNull(term.Value)); Rational x = v[dim]; q += a * x; } return q; }
/// <summary> /// Adds a ray to the frame of "this" and updates Constraints accordingly, see /// Cousot and Halbwachs, section 3.3.1.1. However, this method does not simplify /// Constraints after the operation; that remains the caller's responsibility (which /// gives the caller the opportunity to make multiple calls to AddVertex, AddRay, /// and AddLine before calling SimplifyConstraints). /// Assumes Constraints (and the frame fields) to be non-null. /// </summary> /// <param name="ray"></param> void AddRay(FrameElement/*!*/ ray) { Contract.Requires(ray != null); Contract.Requires(this.FrameRays != null); #if DEBUG_PRINT Console.WriteLine("DEBUG: AddRay called on {0}", ray); Console.WriteLine(" Initial constraints:"); foreach (LinearConstraint cc in Constraints) { Console.WriteLine(" {0}", cc); } #endif FrameRays.Add(ray.Clone()); #if FIXED_DESERIALIZER Contract.Assert(Contract.ForAll(ray.GetDefinedDimensions() , var=> FrameDimensions.Contains(var))); #endif // We use a new temporary dimension. IVariable/*!*/ lambda = new LambdaDimension(); // We change the constraints A*X <= B into // A*X - (A*ray)*lambda <= B. // That means that each row k in A (which corresponds to one LinearConstraint // in Constraints) is changed by subtracting // (A*ray)[k] * lambda // from row k. // Note: // (A*ray)[k] // = { A*ray is a row vector whose every row i is the dot-product of // row i of A with the column vector "ray" } // A[k]*ray foreach (LinearConstraint/*!*/ cc in cce.NonNull(Constraints)) { Contract.Assert(cc != null); Rational d = cc.EvaluateLhs(ray); cc.SetCoefficient(lambda, -d); } // We also add the constraints that lambda is at least 0. LinearConstraint la = new LinearConstraint(LinearConstraint.ConstraintRelation.LE); la.SetCoefficient(lambda, Rational.MINUS_ONE); la.rhs = Rational.ZERO; Constraints.Add(la); #if DEBUG_PRINT Console.WriteLine(" Constraints after addition:"); foreach (LinearConstraint cc in Constraints) { Console.WriteLine(" {0}", cc); } #endif // Finally, project out the dummy dimension. Constraints = Project(lambda, Constraints); #if DEBUG_PRINT Console.WriteLine(" Resulting constraints:"); foreach (LinearConstraint cc in Constraints) { Console.WriteLine(" {0}", cc); } #endif }
/// <summary> /// Adds to "lines" the lines implied by initial-variable columns not in basis /// (see section 3.4.2 of Cousot and Halbwachs), and adds to "constraints" the /// constraints to exclude those lines (see step 4.2 of section 3.4.3 of /// Cousot and Halbwachs). /// </summary> /// <param name="lines"></param> /// <param name="constraints"></param> public void ProduceLines(ArrayList /*FrameElement*//*!*/ lines, ArrayList /*LinearConstraint*//*!*/ constraints) { Contract.Requires(constraints != null); Contract.Requires(lines != null); // for every initial variable not in basis for (int i0 = 0; i0 < numInitialVars; i0++) { if (inBasis[i0] == -1) { FrameElement fe = new FrameElement(); LinearConstraint lc = new LinearConstraint(LinearConstraint.ConstraintRelation.EQ); for (int i = 0; i < numInitialVars; i++) { if (i == i0) { fe.AddCoordinate((IVariable)cce.NonNull(dims[i]), Rational.ONE); lc.SetCoefficient((IVariable)cce.NonNull(dims[i]), Rational.ONE); } else if (inBasis[i] != -1) { // i is a basis column Contract.Assert(m[inBasis[i], i].HasValue(1)); Rational val = -m[inBasis[i], i0]; fe.AddCoordinate((IVariable)cce.NonNull(dims[i]), val); lc.SetCoefficient((IVariable)cce.NonNull(dims[i]), val); } } lines.Add(fe); constraints.Add(lc); } } }
public LinearConstraintSystem/*!*/ Join(LinearConstraintSystem/*!*/ lcs) { Contract.Requires(lcs != null); Contract.Ensures(Contract.Result<LinearConstraintSystem>() != null); #endif if (this.IsBottom()) { return cce.NonNull(lcs.Clone()); } else if (lcs.IsBottom()) { return cce.NonNull(this.Clone()); } else if (this.IsTop() || lcs.IsTop()) { return new LinearConstraintSystem(new ArrayList /*LinearConstraint*/ ()); } else { LinearConstraintSystem/*!*/ z; // Start from the "larger" of the two frames (this is just a heuristic measure intended // to save work). Contract.Assume(this.FrameVertices != null); Contract.Assume(this.FrameRays != null); Contract.Assume(this.FrameLines != null); Contract.Assume(lcs.FrameVertices != null); Contract.Assume(lcs.FrameRays != null); Contract.Assume(lcs.FrameLines != null); if (this.FrameVertices.Count + this.FrameRays.Count + this.FrameLines.Count - this.FrameDimensions.Count < lcs.FrameVertices.Count + lcs.FrameRays.Count + lcs.FrameLines.Count - lcs.FrameDimensions.Count) { z = cce.NonNull(lcs.Clone()); lcs = this; } else { z = cce.NonNull(this.Clone()); } #if DEBUG_PRINT Console.WriteLine("DEBUG: LinearConstraintSystem.Join ---------------"); Console.WriteLine("z:"); z.Dump(); Console.WriteLine("lcs:"); lcs.Dump(); #endif // Start by explicating the implicit lines of z for the dimensions dims(lcs)-dims(z). foreach (IVariable/*!*/ dim in lcs.FrameDimensions) { Contract.Assert(dim != null); if (!z.FrameDimensions.Contains(dim)) { z.FrameDimensions.Add(dim); FrameElement line = new FrameElement(); line.AddCoordinate(dim, Rational.ONE); // Note: AddLine is not called (because the line already exists in z--it's just that // it was represented implicitly). Instead, just tack the explicit representation onto // FrameLines. Contract.Assume(z.FrameLines != null); z.FrameLines.Add(line); #if DEBUG_PRINT Console.WriteLine("Join: After explicating line: {0}", line); z.Dump(); #endif } } // Now, the vertices, rays, and lines can be added. foreach (FrameElement/*!*/ v in lcs.FrameVertices) { Contract.Assert(v != null); z.AddVertex(v); #if DEBUG_PRINT Console.WriteLine("Join: After adding vertex: {0}", v); z.Dump(); #endif } foreach (FrameElement/*!*/ r in lcs.FrameRays) { Contract.Assert(r != null); z.AddRay(r); #if DEBUG_PRINT Console.WriteLine("Join: After adding ray: {0}", r); z.Dump(); #endif } foreach (FrameElement/*!*/ l in lcs.FrameLines) { Contract.Assert(l != null); z.AddLine(l); #if DEBUG_PRINT Console.WriteLine("Join: After adding line: {0}", l); z.Dump(); #endif } // also add to z the implicit lines of lcs foreach (IVariable/*!*/ dim in z.FrameDimensions) { Contract.Assert(dim != null); if (!lcs.FrameDimensions.Contains(dim)) { // "dim" is a dimension that's explicit in "z" but implicit in "lcs" FrameElement line = new FrameElement(); line.AddCoordinate(dim, Rational.ONE); z.AddLine(line); #if DEBUG_PRINT Console.WriteLine("Join: After adding lcs's implicit line: {0}", line); z.Dump(); #endif } } z.SimplifyFrame(); z.SimplifyConstraints(); z.CheckInvariant(); #if DEBUG_PRINT Console.WriteLine("Join: Returning z:"); z.Dump(); Console.WriteLine("----------------------------------------"); #endif return z; } }
public static void RunValidationC() { // Run the example in section 3.4.3 of Cousot and Halbwachs backwards, that is, from // from to constraints. IVariable/*!*/ dim1 = new TestVariable("X"); IVariable/*!*/ dim2 = new TestVariable("Y"); IVariable/*!*/ dim3 = new TestVariable("Z"); Contract.Assert(dim1 != null); Contract.Assert(dim2 != null); Contract.Assert(dim3 != null); FrameElement s0 = new FrameElement(); s0.AddCoordinate(dim1, Rational.ONE); s0.AddCoordinate(dim2, Rational.FromInts(1, 2)); s0.AddCoordinate(dim3, Rational.FromInts(-1, 2)); FrameElement s1 = new FrameElement(); s1.AddCoordinate(dim1, Rational.ONE); s1.AddCoordinate(dim2, Rational.FromInts(-1, 2)); s1.AddCoordinate(dim3, Rational.FromInts(1, 2)); FrameElement s2 = new FrameElement(); s2.AddCoordinate(dim1, Rational.FromInt(3)); s2.AddCoordinate(dim2, Rational.FromInts(-3, 2)); s2.AddCoordinate(dim3, Rational.FromInts(3, 2)); FrameElement r0 = new FrameElement(); r0.AddCoordinate(dim1, Rational.ONE); r0.AddCoordinate(dim2, Rational.FromInts(1, 2)); r0.AddCoordinate(dim3, Rational.FromInts(-1, 2)); FrameElement r1 = new FrameElement(); r1.AddCoordinate(dim1, Rational.ONE); r1.AddCoordinate(dim2, Rational.ZERO); r1.AddCoordinate(dim3, Rational.ZERO); FrameElement d0 = new FrameElement(); d0.AddCoordinate(dim1, Rational.ZERO); d0.AddCoordinate(dim2, Rational.ONE); d0.AddCoordinate(dim3, Rational.ONE); LinearConstraintSystem lcs = new LinearConstraintSystem(s0); lcs.Dump(); lcs.AddVertex(s1); lcs.Dump(); lcs.AddVertex(s2); lcs.Dump(); lcs.AddRay(r0); lcs.Dump(); lcs.AddRay(r1); lcs.Dump(); lcs.AddLine(d0); lcs.Dump(); lcs.SimplifyConstraints(); lcs.Dump(); #if LATER lcs.GenerateFrameFromConstraints(); // should give us back the original frame... #endif }
public static void RunValidationA() { IVariable/*!*/ dim1 = new TestVariable("X"); IVariable/*!*/ dim2 = new TestVariable("Y"); IVariable/*!*/ dim3 = new TestVariable("Z"); Contract.Assert(dim1 != null); Contract.Assert(dim2 != null); Contract.Assert(dim3 != null); FrameElement s1 = new FrameElement(); s1.AddCoordinate(dim1, Rational.ONE); s1.AddCoordinate(dim2, Rational.MINUS_ONE); s1.AddCoordinate(dim3, Rational.ZERO); FrameElement s2 = new FrameElement(); s2.AddCoordinate(dim1, Rational.MINUS_ONE); s2.AddCoordinate(dim2, Rational.ONE); s2.AddCoordinate(dim3, Rational.ZERO); FrameElement r1 = new FrameElement(); r1.AddCoordinate(dim1, Rational.ZERO); r1.AddCoordinate(dim2, Rational.ZERO); r1.AddCoordinate(dim3, Rational.ONE); FrameElement d1 = new FrameElement(); d1.AddCoordinate(dim1, Rational.ONE); d1.AddCoordinate(dim2, Rational.ONE); d1.AddCoordinate(dim3, Rational.ZERO); // create lcs from frame -- cf. Cousot/Halbwachs 1978, section 3.3.1.1 LinearConstraintSystem lcs = new LinearConstraintSystem(s1); lcs.Dump(); lcs.AddVertex(s2); lcs.Dump(); lcs.AddRay(r1); lcs.Dump(); lcs.AddLine(d1); lcs.Dump(); lcs.SimplifyConstraints(); lcs.Dump(); #if LATER lcs.GenerateFrameFromConstraints(); // should give us back the original frame... #endif Console.WriteLine("IsSubset? {0}", lcs.IsSubset(lcs.Clone())); lcs.Dump(); }
LinearConstraintSystem(FrameElement/*!*/ v) { Contract.Requires(v != null); IMutableSet/*!*/ frameDims = v.GetDefinedDimensions(); Contract.Assert(frameDims != null); ArrayList /*LinearConstraint!*/ constraints = new ArrayList /*LinearConstraint!*/ (); foreach (IVariable/*!*/ dim in frameDims) { Contract.Assert(dim != null); LinearConstraint lc = new LinearConstraint(LinearConstraint.ConstraintRelation.EQ); lc.SetCoefficient(dim, Rational.ONE); lc.rhs = v[dim]; constraints.Add(lc); } FrameDimensions = frameDims; Constraints = constraints; ArrayList /*FrameElement*/ frameVertices = new ArrayList /*FrameElement*/ (); frameVertices.Add(v); FrameVertices = frameVertices; FrameRays = new ArrayList /*FrameElement*/ (); FrameLines = new ArrayList /*FrameElement*/ (); //:base(); CheckInvariant(); }
/// <summary> /// Adds a line to the frame of "this" and updates Constraints accordingly, see /// Cousot and Halbwachs, section 3.3.1.1. However, this method does not simplify /// Constraints after the operation; that remains the caller's responsibility (which /// gives the caller the opportunity to make multiple calls to AddVertex, AddRay, /// and AddLine before calling SimplifyConstraints). /// Assumes Constraints (and the frame fields) to be non-null. /// </summary> /// <param name="line"></param> void AddLine(FrameElement/*!*/ line) { Contract.Requires(line != null); Contract.Requires(this.FrameLines != null); // Note: The code for AddLine is identical to that of AddRay, except the AddLine // does not introduce the constraint 0 <= lambda. (One could imagine sharing the // code between AddRay and AddLine.) #if DEBUG_PRINT Console.WriteLine("DEBUG: AddLine called on {0}", line); Console.WriteLine(" Initial constraints:"); foreach (LinearConstraint cc in Constraints) { Console.WriteLine(" {0}", cc); } #endif FrameLines.Add(line.Clone()); #if FIXED_DESERIALIZER Contract.Assert(Contract.ForAll(line.GetDefinedDimensions() , var=> FrameDimensions.Contains(var))); #endif // We use a new temporary dimension. IVariable/*!*/ lambda = new LambdaDimension(); // We change the constraints A*X <= B into // A*X - (A*line)*lambda <= B. // That means that each row k in A (which corresponds to one LinearConstraint // in Constraints) is changed by subtracting // (A*line)[k] * lambda // from row k. // Note: // (A*line)[k] // = { A*line is a row vector whose every row i is the dot-product of // row i of A with the column vector "line" } // A[k]*line foreach (LinearConstraint/*!*/ cc in cce.NonNull(Constraints)) { Contract.Assert(cc != null); Rational d = cc.EvaluateLhs(line); cc.SetCoefficient(lambda, -d); } #if DEBUG_PRINT Console.WriteLine(" Constraints after addition:"); foreach (LinearConstraint cc in Constraints) { Console.WriteLine(" {0}", cc); } #endif // Finally, project out the dummy dimension. Constraints = Project(lambda, Constraints); #if DEBUG_PRINT Console.WriteLine(" Resulting constraints:"); foreach (LinearConstraint cc in Constraints) { Console.WriteLine(" {0}", cc); } #endif }
public FrameElement Rename(IVariable/*!*/ oldName, IVariable/*!*/ newName) { Contract.Requires(newName != null); Contract.Requires(oldName != null); object /*Rational*/ z = terms[oldName]; if (z == null) { return this; } else { System.Diagnostics.Debug.Assert(z is Rational); Hashtable /*IVariable->Rational*/ newTerms = (Hashtable/*!*/ /*IVariable->Rational*/)cce.NonNull(terms.Clone()); newTerms.Remove(oldName); newTerms.Add(newName, z); FrameElement fe = new FrameElement(); fe.terms = newTerms; return fe; } }
public FrameElement Clone() { FrameElement z = new FrameElement(); z.terms = (Hashtable /*IVariable->Rational*/)this.terms.Clone(); return z; }
/// <summary> /// Worker method of TraverseVertices. /// This method has no net effect on the tableau. /// </summary> /// <param name="basesSeenSoFar"></param> /// <param name="vertices"></param> /// <param name="rays"></param> void TraverseBases(ArrayList /*bool[]*//*!*/ basesSeenSoFar, ArrayList /*FrameElement*//*!*/ vertices, ArrayList /*FrameElement*//*!*/ rays) { Contract.Requires(rays != null); Contract.Requires(vertices != null); Contract.Requires(basesSeenSoFar != null); CheckInvariant(); bool[] thisBasis = new bool[numSlackVars]; for (int i = numInitialVars; i < rhsColumn; i++) { if (inBasis[i] != -1) { thisBasis[i - numInitialVars] = true; } } foreach (bool[] /*!*/ basis in basesSeenSoFar) { Contract.Assert(basis != null); Contract.Assert(basis.Length == numSlackVars); for (int i = 0; i < numSlackVars; i++) { if (basis[i] != thisBasis[i]) { goto COMPARE_WITH_NEXT_BASIS; } } // thisBasis and basis are the same--that is, basisColumns has been visited before--so // we don't traverse anything from here return; COMPARE_WITH_NEXT_BASIS : { } } // basisColumns has not been seen before; record thisBasis and continue with the traversal here basesSeenSoFar.Add(thisBasis); #if DEBUG_PRINT Console.Write("TraverseBases, new basis: "); foreach (bool t in thisBasis) { Console.Write("{0}", t ? "*" : "."); } Console.WriteLine(); Dump(); #endif // Add vertex FrameElement v = new FrameElement(); for (int i = 0; i < rows; i++) { int j = basisColumns[i]; if (j < numInitialVars) { v.AddCoordinate((IVariable)cce.NonNull(dims[j]), m[i, rhsColumn]); } } #if DEBUG_PRINT Console.WriteLine(" Adding vertex: {0}", v); #endif vertices.Add(v); // Add rays. Traverse all columns corresponding to slack variables that // are not in basis (see second bullet of section 3.4.2 of Cousot and Halbwachs). for (int i0 = numInitialVars; i0 < rhsColumn; i0++) { if (inBasis[i0] != -1) { // skip those slack-variable columns that are in basis continue; } // check if slack-variable, non-basis column i corresponds to an extreme ray for (int row = 0; row < rows; row++) { if (m[row, i0].IsPositive) { for (int k = numInitialVars; k < rhsColumn; k++) { if (inBasis[k] != -1 && m[row, k].IsNonZero) { // does not correspond to an extreme ray goto CHECK_NEXT_SLACK_VAR; } } } } // corresponds to an extreme ray FrameElement ray = new FrameElement(); for (int i = 0; i < numInitialVars; i++) { int j0 = inBasis[i]; Rational val = -m[j0, i0]; ray.AddCoordinate((IVariable)cce.NonNull(dims[i]), val); } #if DEBUG_PRINT Console.WriteLine(" Adding ray: {0}", ray); #endif rays.Add(ray); CHECK_NEXT_SLACK_VAR : { } } // Continue traversal for (int i = numInitialVars; i < rhsColumn; i++) { int j = inBasis[i]; if (j != -1) { // try moving i out of basis and some other slack-variable column into basis for (int k = numInitialVars; k < rhsColumn; k++) { if (inBasis[k] == -1 && m[j, k].IsPositive) { Rational[] undo = Pivot(j, k); // check if the new basis is feasible for (int p = 0; p < rows; p++) { int c = basisColumns[p]; if (numInitialVars <= c && c < rhsColumn && m[p, rhsColumn].IsNegative) { // not feasible goto AFTER_TRAVERSE; } } TraverseBases(basesSeenSoFar, vertices, rays); AFTER_TRAVERSE: UnPivot(j, k, undo); } } } } }
/// <summary> /// Worker method of TraverseVertices. /// This method has no net effect on the tableau. /// </summary> /// <param name="basesSeenSoFar"></param> /// <param name="vertices"></param> /// <param name="rays"></param> void TraverseBases(ArrayList /*bool[]*//*!*/ basesSeenSoFar, ArrayList /*FrameElement*//*!*/ vertices, ArrayList /*FrameElement*//*!*/ rays) { Contract.Requires(rays != null); Contract.Requires(vertices != null); Contract.Requires(basesSeenSoFar != null); CheckInvariant(); bool[] thisBasis = new bool[numSlackVars]; for (int i = numInitialVars; i < rhsColumn; i++) { if (inBasis[i] != -1) { thisBasis[i - numInitialVars] = true; } } foreach (bool[]/*!*/ basis in basesSeenSoFar) { Contract.Assert(basis != null); Contract.Assert(basis.Length == numSlackVars); for (int i = 0; i < numSlackVars; i++) { if (basis[i] != thisBasis[i]) { goto COMPARE_WITH_NEXT_BASIS; } } // thisBasis and basis are the same--that is, basisColumns has been visited before--so // we don't traverse anything from here return; COMPARE_WITH_NEXT_BASIS: { } } // basisColumns has not been seen before; record thisBasis and continue with the traversal here basesSeenSoFar.Add(thisBasis); #if DEBUG_PRINT Console.Write("TraverseBases, new basis: "); foreach (bool t in thisBasis) { Console.Write("{0}", t ? "*" : "."); } Console.WriteLine(); Dump(); #endif // Add vertex FrameElement v = new FrameElement(); for (int i = 0; i < rows; i++) { int j = basisColumns[i]; if (j < numInitialVars) { v.AddCoordinate((IVariable)cce.NonNull(dims[j]), m[i, rhsColumn]); } } #if DEBUG_PRINT Console.WriteLine(" Adding vertex: {0}", v); #endif vertices.Add(v); // Add rays. Traverse all columns corresponding to slack variables that // are not in basis (see second bullet of section 3.4.2 of Cousot and Halbwachs). for (int i0 = numInitialVars; i0 < rhsColumn; i0++) { if (inBasis[i0] != -1) { // skip those slack-variable columns that are in basis continue; } // check if slack-variable, non-basis column i corresponds to an extreme ray for (int row = 0; row < rows; row++) { if (m[row, i0].IsPositive) { for (int k = numInitialVars; k < rhsColumn; k++) { if (inBasis[k] != -1 && m[row, k].IsNonZero) { // does not correspond to an extreme ray goto CHECK_NEXT_SLACK_VAR; } } } } // corresponds to an extreme ray FrameElement ray = new FrameElement(); for (int i = 0; i < numInitialVars; i++) { int j0 = inBasis[i]; Rational val = -m[j0, i0]; ray.AddCoordinate((IVariable)cce.NonNull(dims[i]), val); } #if DEBUG_PRINT Console.WriteLine(" Adding ray: {0}", ray); #endif rays.Add(ray); CHECK_NEXT_SLACK_VAR: { } } // Continue traversal for (int i = numInitialVars; i < rhsColumn; i++) { int j = inBasis[i]; if (j != -1) { // try moving i out of basis and some other slack-variable column into basis for (int k = numInitialVars; k < rhsColumn; k++) { if (inBasis[k] == -1 && m[j, k].IsPositive) { Rational[] undo = Pivot(j, k); // check if the new basis is feasible for (int p = 0; p < rows; p++) { int c = basisColumns[p]; if (numInitialVars <= c && c < rhsColumn && m[p, rhsColumn].IsNegative) { // not feasible goto AFTER_TRAVERSE; } } TraverseBases(basesSeenSoFar, vertices, rays); AFTER_TRAVERSE: UnPivot(j, k, undo); } } } } }
/// <summary> /// Adds a vertex to the frame of "this" and updates Constraints accordingly, see /// Cousot and Halbwachs, section 3.3.1.1. However, this method does not simplify /// Constraints after the operation; that remains the caller's responsibility (which /// gives the caller the opportunity to make multiple calls to AddVertex, AddRay, /// and AddLine before calling SimplifyConstraints). /// Assumes Constraints (and the frame fields) to be non-null. /// </summary> /// <param name="vertex"></param> void AddVertex(FrameElement/*!*/ vertex) { Contract.Requires(vertex != null); Contract.Requires(this.FrameVertices != null); #if DEBUG_PRINT Console.WriteLine("DEBUG: AddVertex called on {0}", vertex); Console.WriteLine(" Initial constraints:"); foreach (LinearConstraint cc in Constraints) { Console.WriteLine(" {0}", cc); } #endif FrameVertices.Add(vertex.Clone()); #if FIXED_DESERIALIZER Contract.Assert(Contract.ForAll(vertex.GetDefinedDimensions() , var=> FrameDimensions.Contains(var))); #endif // We use a new temporary dimension. IVariable/*!*/ lambda = new LambdaDimension(); // We change the constraints A*X <= B into // A*X + (A*vector - B)*lambda <= A*vector. // That means that each row k in A (which corresponds to one LinearConstraint // in Constraints) is changed by adding // (A*vector - B)[k] * lambda // to row k and changing the right-hand side of row k to // (A*vector)[k] // Note: // (A*vector - B)[k] // = { vector subtraction is pointwise } // (A*vector)[k] - B[k] // = { A*vector is a row vector whose every row i is the dot-product of // row i of A with the column vector "vector" } // A[k]*vector - B[k] foreach (LinearConstraint/*!*/ cc in cce.NonNull(Constraints)) { Contract.Assert(cc != null); Rational d = cc.EvaluateLhs(vertex); cc.SetCoefficient(lambda, d - cc.rhs); cc.rhs = d; } // We also add the constraints that lambda lies between 0 ... LinearConstraint la = new LinearConstraint(LinearConstraint.ConstraintRelation.LE); la.SetCoefficient(lambda, Rational.MINUS_ONE); la.rhs = Rational.ZERO; Constraints.Add(la); // ... and 1. la = new LinearConstraint(LinearConstraint.ConstraintRelation.LE); la.SetCoefficient(lambda, Rational.ONE); la.rhs = Rational.ONE; Constraints.Add(la); #if DEBUG_PRINT Console.WriteLine(" Constraints after addition:"); foreach (LinearConstraint cc in Constraints) { Console.WriteLine(" {0}", cc); } #endif // Finally, project out the dummy dimension. Constraints = Project(lambda, Constraints); #if DEBUG_PRINT Console.WriteLine(" Resulting constraints:"); foreach (LinearConstraint cc in Constraints) { Console.WriteLine(" {0}", cc); } #endif }