//----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //----------------------------------------------------------------------------- namespace Microsoft.AbstractInterpretationFramework { using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System; //using Microsoft.SpecSharp.Collections; using System.Diagnostics.Contracts; using Microsoft.Basetypes; using IMutableSet = Microsoft.Boogie.Set; using HashSet = Microsoft.Boogie.Set; using ISet = Microsoft.Boogie.Set; /// /// Represents a system of linear constraints (constraint/frame representations). /// public class LinearConstraintSystem { // -------------------------------------------------------------------------------------------------------- // ------------------ Data structure ---------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------- public /*maybe null*/ ArrayList /*LinearConstraint!*/ Constraints; /*maybe null*/ ArrayList /*FrameElement!*/ FrameVertices; /*maybe null*/ ArrayList /*FrameElement!*/ FrameRays; IMutableSet/*IVariable!*//*!*/ FrameDimensions; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(FrameDimensions != null); } /*maybe null*/ ArrayList /*FrameElement!*/ FrameLines; // Invariant: Either all of Constraints, FrameVertices, FrameRays, and FrameLines are // null, or all are non-null. // Invariant: Any dimension mentioned in Constraints, FrameVertices, FrameRays, or // FrameLines is mentioned in FrameDimensions. // The meaning of FrameDimensions is that for any dimension x not in FrameDimensions, // there is an implicit line along dimension x (that is, ()). void CheckInvariant() { if (Constraints == null) { System.Diagnostics.Debug.Assert(FrameVertices == null); System.Diagnostics.Debug.Assert(FrameRays == null); System.Diagnostics.Debug.Assert(FrameLines == null); System.Diagnostics.Debug.Assert(FrameDimensions.Count == 0); } else { System.Diagnostics.Debug.Assert(FrameVertices != null); System.Diagnostics.Debug.Assert(FrameRays != null); System.Diagnostics.Debug.Assert(FrameLines != null); foreach (LinearConstraint/*!*/ cc in Constraints) { Contract.Assert(cc != null); #if FIXED_DESERIALIZER Contract.Assert(Contract.ForAll(cc.GetDefinedDimensions() , var=> FrameDimensions.Contains(var))); #endif Contract.Assert(cc.coefficients.Count != 0); } foreach (ArrayList /*FrameElement*//*!*/ FrameComponent in new ArrayList /*FrameElement*/ [] { FrameVertices, FrameRays, FrameLines }) { Contract.Assert(FrameComponent != null); foreach (FrameElement fe in FrameComponent) { if (fe == null) continue; #if FIXED_DESERIALIZER Contract.Assert(Contract.ForAll(fe.GetDefinedDimensions() , var=> FrameDimensions.Contains(var))); #endif } } } } // -------------------------------------------------------------------------------------------------------- // ------------------ Constructors ------------------------------------------------------------------------ // -------------------------------------------------------------------------------------------------------- /// /// Creates a LinearConstraintSystem representing the bottom element, that is, representing /// an unsatisfiable system of constraints. /// [NotDelayed] public LinearConstraintSystem() { FrameDimensions = new HashSet /*IVariable!*/ (); //:base(); CheckInvariant(); } /// /// Constructs a linear constraint system with constraints "cs". /// The constructor captures all constraints in "cs". /// /// [NotDelayed] public LinearConstraintSystem(ArrayList /*LinearConstraint!*//*!*/ cs) { Contract.Requires(cs != null); #if BUG_159_HAS_BEEN_FIXED Contract.Requires(Contract.ForAll(cs) , cc=> cc.coefficients.Count != 0); #endif ArrayList constraints = new ArrayList /*LinearConstraint!*/ (cs.Count); foreach (LinearConstraint/*!*/ cc in cs) { Contract.Assert(cc != null); constraints.Add(cc); } Constraints = constraints; FrameDimensions = new HashSet /*IVariable!*/ (); // to please compiler; this value will be overridden in the call to GenerateFrameConstraints below //:base(); GenerateFrameFromConstraints(); SimplifyConstraints(); CheckInvariant(); #if DEBUG_PRINT Console.WriteLine("LinearConstraintSystem: constructor produced:"); Dump(); #endif } /// /// Constructs a linear constraint system corresponding to given vertex. This constructor /// is only used in the test harness--it is not needed for abstract interpretation. /// /// [NotDelayed] 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(); } void ChangeIntoBottom() { Constraints = null; FrameVertices = null; FrameRays = null; FrameLines = null; IMutableSet ss; FrameDimensions.Clear(); // no implicit lines } // -------------------------------------------------------------------------------------------------------- // ------------------ Public operations and their support routines ---------------------------------------- // -------------------------------------------------------------------------------------------------------- public bool IsBottom() { return Constraints == null; } public bool IsTop() { return Constraints != null && Constraints.Count == 0; } [Pure] public override string/*!*/ ToString() { Contract.Ensures(Contract.Result() != null); if (Constraints == null) { return ""; } else if (Constraints.Count == 0) { return ""; } else { string z = null; foreach (LinearConstraint/*!*/ lc in Constraints) { Contract.Assert(lc != null); string s = lc.ToString(); if (z == null) { z = s; } else { z += " AND " + s; } } Contract.Assert(z != null); return z; } } public ICollection/*!*/ FreeVariables() { Contract.Ensures(cce.NonNullElements(Contract.Result>())); Contract.Ensures(Contract.Result>().IsReadOnly); List list = new List(); foreach (IVariable/*!*/ v in FrameDimensions) { Contract.Assert(v != null); list.Add(v); } return cce.NonNull(list.AsReadOnly()); } /// /// Note: This method requires that all dimensions are of type Variable, something that's /// not required elsewhere in this class. /// /// public IExpr/*!*/ ConvertToExpression(ILinearExprFactory/*!*/ factory) { Contract.Requires(factory != null); Contract.Ensures(Contract.Result() != null); if (this.Constraints == null) { return factory.False; } if (this.Constraints.Count == 0) { return factory.True; } IExpr result = null; foreach (LinearConstraint/*!*/ lc in Constraints) { Contract.Assert(lc != null); IExpr conjunct = lc.ConvertToExpression(factory); result = (result == null) ? conjunct : (IExpr)factory.And(conjunct, result); } Contract.Assert(result != null); return result; } /* IsSubset(): determines if 'lcs' is a subset of 'this' * -- See Cousot/Halbwachs 1978, section */ public bool IsSubset(LinearConstraintSystem/*!*/ lcs) { Contract.Requires(lcs != null); if (lcs.IsBottom()) { return true; } else if (this.IsBottom()) { return false; #if DEBUG #else } else if (this.IsTop()) { // optimization -- this case not needed for correctness return true; } else if (lcs.IsTop()) { // optimization -- this case not needed for correctness return false; #endif } else { // phase 0: check if frame dimensions are a superset of the constraint dimensions ISet /*IVariable!*//*!*/ frameDims = lcs.GetDefinedDimensions(); Contract.Assert(frameDims != null); #if DEBUG_PRINT Console.WriteLine("DEBUG: IsSubset:"); Console.WriteLine(" --- this:"); this.Dump(); Console.WriteLine(" --- lcs:"); lcs.Dump(); Console.WriteLine(" ---"); #endif foreach (LinearConstraint/*!*/ cc in cce.NonNull(this.Constraints)) { Contract.Assert(cc != null); #if DEBUG_PRINT Console.WriteLine(" cc: {0}", cc); Console.WriteLine(" cc.GetDefinedDimensions(): {0}", cc.GetDefinedDimensions()); #endif if (!Contract.ForAll(cc.GetDefinedDimensionsGeneric(), var => frameDims.Contains(var))) { #if DEBUG_PRINT Console.WriteLine(" ---> phase 0 subset violated, return false from IsSubset"); #endif return false; } } } // phase 1: check frame vertices against each constraint... foreach (FrameElement/*!*/ v in cce.NonNull(lcs.FrameVertices)) { Contract.Assert(v != null); foreach (LinearConstraint/*!*/ cc in this.Constraints) { Contract.Assert(cc != null); Rational q = cc.EvaluateLhs(v); if (cc.Relation == LinearConstraint.ConstraintRelation.LE) { if (!(q <= cc.rhs)) { #if DEBUG_PRINT Console.WriteLine(" ---> phase 1a subset violated, return false from IsSubset"); #endif return false; } } else { if (!(q == cc.rhs)) { #if DEBUG_PRINT Console.WriteLine(" ---> phase 1b subset violated, return false from IsSubset"); #endif return false; } } } } // phase 2: check frame rays against each constraint... // To check if a ray "r" falls within a constraint "cc", we add the vector "r" to // any point "p" on the side of the half-space or plane described by constraint, and // then check if the resulting point satisfies the constraint. That is, we check (for // an inequality constraint with coefficients a1,a2,...,an and right-hand side // constant C): // a1*(r1+p1) + a2*(r2+p2) + ... + an*(rn+pn) <= C // Equivalently: // a1*r1 + a2*r2 + ... + an*rn + a1*p1 + a2*p2 + ... + an*pn <= C // To find a point "p", we can pick out a coordinate, call it 1, with a non-zero // coefficient in the constraint, and then choose "p" as the point that has the // value C/a1 in coordinate 1 and has 0 in all other coordinates. We then check: // a1*r1 + a2*r2 + ... + an*rn + a1*(C/a1) + a2*0 + ... + an*0 <= C // which simplifies to: // a1*r1 + a2*r2 + ... + an*rn + C <= C // which in turn simplifies to: // a1*r1 + a2*r2 + ... + an*rn <= 0 // If the constraint is an equality constraint, we simply replace "<=" with "==" // above. foreach (FrameElement/*!*/ r in cce.NonNull(lcs.FrameRays)) { Contract.Assert(r != null); System.Diagnostics.Debug.Assert(r != null, "encountered a null ray..."); foreach (LinearConstraint/*!*/ cc in this.Constraints) { Contract.Assert(cc != null); System.Diagnostics.Debug.Assert(cc != null, "encountered an null constraint..."); Rational q = cc.EvaluateLhs(r); if (cc.Relation == LinearConstraint.ConstraintRelation.LE) { if (q.IsPositive) { #if DEBUG_PRINT Console.WriteLine(" ---> phase 2a subset violated, return false from IsSubset"); #endif return false; } } else { if (q.IsNonZero) { #if DEBUG_PRINT Console.WriteLine(" ---> phase 2b subset violated, return false from IsSubset"); #endif return false; } } } } // phase 3: check frame lines against each constraint... // To check if a line "L" falls within a constraint "cc", we check if both the // vector "L" and "-L", interpreted as rays, fall within the constraint. From // the discussion above, this means we check the following two properties: // a1*L1 + a2*L2 + ... + an*Ln <= 0 (*) // a1*(-L1) + a2*(-L2) + ... + an*(-Ln) <= 0 // The second of these lines can be rewritten as: // - a1*L1 - a2*L2 - ... - an*Ln <= 0 // which is equivalent to: // -1 * (a1*L1 + a2*L2 + ... + an*Ln) <= 0 // Multiplying both sides by -1 and flipping the direction of the inequality, // we have: // a1*L1 + a2*L2 + ... + an*Ln >= 0 (**) // Putting (*) and (**) together, we conclude that we need to check: // a1*L1 + a2*L2 + ... + an*Ln == 0 // If the constraint is an equality constraint, we end up with the same equation. foreach (FrameElement/*!*/ line in cce.NonNull(lcs.FrameLines)) { Contract.Assert(line != null); System.Diagnostics.Debug.Assert(line != null, "encountered a null line..."); foreach (LinearConstraint/*!*/ cc in this.Constraints) { Contract.Assert(cc != null); System.Diagnostics.Debug.Assert(cc != null, "encountered an null constraint..."); Rational q = cc.EvaluateLhs(line); if (q.IsNonZero) { #if DEBUG_PRINT Console.WriteLine(" ---> phase 3 subset violated, return false from IsSubset"); #endif return false; } } } #if DEBUG_PRINT Console.WriteLine(" ---> IsSubset returns true"); #endif return true; } public LinearConstraintSystem/*!*/ Meet(LinearConstraintSystem/*!*/ lcs) { Contract.Requires(lcs != null); Contract.Requires((this.Constraints != null)); Contract.Requires((lcs.Constraints != null)); Contract.Ensures(Contract.Result() != null); ArrayList /*LinearConstraint*/ clist = new ArrayList(this.Constraints.Count + lcs.Constraints.Count); clist.AddRange(this.Constraints); clist.AddRange(lcs.Constraints); return new LinearConstraintSystem(clist); } #if DEBUG_PRINT public LinearConstraintSystem Join(LinearConstraintSystem lcs) { Console.WriteLine("==================================================================================="); Console.WriteLine("DEBUG: Join"); Console.WriteLine("Join: this="); Dump(); Console.WriteLine("Join: lcs="); lcs.Dump(); LinearConstraintSystem z = JoinX(lcs); Console.WriteLine("----------Join------------------------------>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); Console.WriteLine("Join: result="); z.Dump(); Console.WriteLine("==================================================================================="); return z; } #endif /// /// The join is computed as described in section 4.4 in Cousot and Halbwachs. /// /// /// #if DEBUG_PRINT public LinearConstraintSystem JoinX(LinearConstraintSystem lcs) { #else public LinearConstraintSystem/*!*/ Join(LinearConstraintSystem/*!*/ lcs) { Contract.Requires(lcs != null); Contract.Ensures(Contract.Result() != 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; } } #if DEBUG_PRINT public LinearConstraintSystem Widen(LinearConstraintSystem lcs) { Console.WriteLine("==================================================================================="); Console.WriteLine("DEBUG: Widen"); Console.WriteLine("Widen: this="); Dump(); Console.WriteLine("Widen: lcs="); lcs.Dump(); LinearConstraintSystem z = WidenX(lcs); Console.WriteLine("----------Widen------------------------------>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); Console.WriteLine("Widen: result="); z.Dump(); Console.WriteLine("==================================================================================="); return z; } #endif #if DEBUG_PRINT public LinearConstraintSystem WidenX(LinearConstraintSystem lcs){ #else public LinearConstraintSystem/*!*/ Widen(LinearConstraintSystem/*!*/ lcs) { Contract.Requires(lcs != null); Contract.Ensures(Contract.Result() != 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*/ ()); } // create new LCS, we will add only verified constraints to this... ArrayList /*LinearConstraint*/ newConstraints = new ArrayList /*LinearConstraint*/ (); Contract.Assume(this.Constraints != null); foreach (LinearConstraint/*!*/ ccX in this.Constraints) { Contract.Assert(ccX != null); LinearConstraint cc = ccX; #if DEBUG_PRINT Console.WriteLine("Widen checking: Starting to check constraint: {0}", cc); #endif if (cc.IsConstant()) { // (Can this ever occur in the stable state of a LinearConstraintSystem? --KRML) // constraint is unaffected by the frame components #if DEBUG_PRINT Console.WriteLine("Widen checking: --Adding it!"); #endif newConstraints.Add(cc); continue; } // PHASE I: verify constraints against all frame vertices... foreach (FrameElement/*!*/ vertex in cce.NonNull(lcs.FrameVertices)) { Contract.Assert(vertex != null); Rational lhs = cc.EvaluateLhs(vertex); if (lhs > cc.rhs) { // the vertex does not satisfy the inequality <= if (cc.Relation == LinearConstraint.ConstraintRelation.LE) { #if DEBUG_PRINT Console.WriteLine("Widen checking: throwing out because of vertex: {0}", vertex); #endif goto CHECK_NEXT_CONSTRAINT; } else { // ... but it does satisfy the inequality >= #if DEBUG_PRINT Console.WriteLine("Widen checking: throwing out <= because of vertex: {0}", vertex); #endif cc = cc.ChangeRelationToAtLeast(); #if DEBUG_PRINT Console.WriteLine("Widen checking: left with constraint: {0}", cc); #endif } } else if (cc.Relation == LinearConstraint.ConstraintRelation.EQ && lhs < cc.rhs) { // the vertex does not satisfy the inequality >=, and the constraint is an equality constraint #if DEBUG_PRINT Console.WriteLine("Widen checking: throwing out >= because of vertex: {0}", vertex); #endif cc = cc.ChangeRelation(LinearConstraint.ConstraintRelation.LE); #if DEBUG_PRINT Console.WriteLine("Widen checking: left with contraint: {0}", cc); #endif } } // PHASE II: verify constraints against all frame rays... foreach (FrameElement/*!*/ ray in cce.NonNull(lcs.FrameRays)) { Contract.Assert(ray != null); // The following assumes the constraint to have some dimension with a non-zero coefficient Rational lhs = cc.EvaluateLhs(ray); if (lhs.IsPositive) { // the ray does not satisfy the inequality <= if (cc.Relation == LinearConstraint.ConstraintRelation.LE) { #if DEBUG_PRINT Console.WriteLine("Widen checking: throwing out because of ray: {0}", ray); #endif goto CHECK_NEXT_CONSTRAINT; } else { // ... but it does satisfy the inequality >= #if DEBUG_PRINT Console.WriteLine("Widen checking: throwing out <= because of ray: {0}", ray); #endif cc = cc.ChangeRelationToAtLeast(); #if DEBUG_PRINT Console.WriteLine("Widen checking: left with contraint: {0}", cc); #endif } } else if (cc.Relation == LinearConstraint.ConstraintRelation.EQ && lhs.IsNegative) { // the ray does not satisfy the inequality >=, and the constraint is an equality constraint #if DEBUG_PRINT Console.WriteLine("Widen checking: throwing out >= because of ray: {0}", ray); #endif cc = cc.ChangeRelation(LinearConstraint.ConstraintRelation.LE); #if DEBUG_PRINT Console.WriteLine("Widen checking: left with constraint: {0}", cc); #endif } } // PHASE III: verify constraints against all frame lines... foreach (FrameElement/*!*/ line in cce.NonNull(lcs.FrameLines)) { Contract.Assert(line != null); // The following assumes the constraint to have some dimension with a non-zero coefficient Rational lhs = cc.EvaluateLhs(line); if (!lhs.IsZero) { // The line satisfies neither the inequality <= nor the equality == #if DEBUG_PRINT Console.WriteLine("Widen checking: throwing out because of line: {0}", line); #endif goto CHECK_NEXT_CONSTRAINT; } } // constraint has been verified, so add to new constraint system #if DEBUG_PRINT Console.WriteLine("Widen checking: --Adding it!"); #endif newConstraints.Add(cc); CHECK_NEXT_CONSTRAINT: { } #if DEBUG_PRINT Console.WriteLine("Widen checking: done with that constraint"); #endif } return new LinearConstraintSystem(newConstraints); } #if DEBUG_PRINT public LinearConstraintSystem Project(IVariable/*!*/ dim){ Contract.Requires(dim != null); Console.WriteLine("==================================================================================="); Console.WriteLine("DEBUG: Project(dim={0})", dim); Console.WriteLine("Project: this="); Dump(); LinearConstraintSystem z = ProjectX(dim); Console.WriteLine("----------Project------------------------------>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); Console.WriteLine("Project: result="); z.Dump(); Console.WriteLine("==================================================================================="); return z; } #endif #if DEBUG_PRINT public LinearConstraintSystem ProjectX(IVariable/*!*/ dim){Contract.Requires(dim != null);Contract.Requires(this.Constraints != null); #else public LinearConstraintSystem/*!*/ Project(IVariable/*!*/ dim) { Contract.Requires(dim != null); Contract.Requires(this.Constraints != null); Contract.Ensures(Contract.Result() != null); #endif ArrayList /*LinearConstraint!*//*!*/ cc = Project(dim, Constraints); Contract.Assert(cc != null); return new LinearConstraintSystem(cc); } #if DEBUG_PRINT public LinearConstraintSystem Rename(IVariable/*!*/ oldName, IVariable/*!*/ newName){ Contract.Requires(newName != null); Contract.Requires(oldName != null); Console.WriteLine("==================================================================================="); Console.WriteLine("DEBUG: Rename(oldName={0}, newName={1})", oldName, newName); Console.WriteLine("Rename: this="); Dump(); LinearConstraintSystem z = RenameX(oldName, newName); Console.WriteLine("----------Rename------------------------------>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); Console.WriteLine("Rename: result="); z.Dump(); Console.WriteLine("==================================================================================="); return z; } #endif #if DEBUG_PRINT public LinearConstraintSystem RenameX(IVariable/*!*/ oldName, IVariable/*!*/ newName){Contract.Requires(oldName != null);Contract.Requires(newName != null); #else public LinearConstraintSystem/*!*/ Rename(IVariable/*!*/ oldName, IVariable/*!*/ newName) { Contract.Requires(oldName != null); Contract.Requires(newName != null); Contract.Ensures(Contract.Result() != null); #endif if (this.Constraints == null) { System.Diagnostics.Debug.Assert(this.FrameVertices == null); System.Diagnostics.Debug.Assert(this.FrameRays == null); System.Diagnostics.Debug.Assert(this.FrameLines == null); return this; } IMutableSet /*IVariable!*//*!*/ dims = this.FrameDimensions; Contract.Assert(dims != null); if (!dims.Contains(oldName)) { return this; } LinearConstraintSystem z = new LinearConstraintSystem(); z.FrameDimensions = cce.NonNull((HashSet/*!*/ /*IVariable!*/)dims.Clone()); z.FrameDimensions.Remove(oldName); z.FrameDimensions.Add(newName); z.Constraints = new ArrayList /*LinearConstraint!*/ (this.Constraints.Count); foreach (LinearConstraint/*!*/ lc in cce.NonNull(this.Constraints)) { Contract.Assert(lc != null); z.Constraints.Add(lc.Rename(oldName, newName)); } z.FrameVertices = RenameInFE(cce.NonNull(this.FrameVertices), oldName, newName); z.FrameRays = RenameInFE(cce.NonNull(this.FrameRays), oldName, newName); z.FrameLines = RenameInFE(cce.NonNull(this.FrameLines), oldName, newName); return z; } static ArrayList /*FrameElement*/ RenameInFE(ArrayList/*!*/ /*FrameElement*/ list, IVariable/*!*/ oldName, IVariable/*!*/ newName) { Contract.Requires(list != null); Contract.Requires(newName != null); Contract.Requires(oldName != null); ArrayList/*FrameElement!*//*!*/ z = new ArrayList/*FrameElement!*/ (list.Count); Contract.Assert(z != null); foreach (FrameElement/*!*/ fe in list) { Contract.Assert(fe != null); z.Add(fe.Rename(oldName, newName)); } System.Diagnostics.Debug.Assert(z.Count == list.Count); return z; } // -------------------------------------------------------------------------------------------------------- // ------------------ support routines -------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------- /// /// Returns a set of constraints that is the given set of constraints with dimension "dim" /// projected out. See Cousot and Halbwachs, section 3.3.1.1. /// /// /// /// static ArrayList /*LinearConstraint!*//*!*/ Project(IVariable/*!*/ dim, ArrayList /*LinearConstraint!*//*!*/ constraints) { Contract.Requires(constraints != null); Contract.Requires(dim != null); Contract.Ensures(Contract.Result() != null); // Sort the inequality constaints into ones where dimension "dim" is 0, negative, and // positive, respectively. Put equality constraints with a non-0 "dim" into "eq". ArrayList /*LinearConstraint!*//*!*/ final = new ArrayList /*LinearConstraint!*/ (); ArrayList /*LinearConstraint!*//*!*/ negative = new ArrayList /*LinearConstraint!*/ (); ArrayList /*LinearConstraint!*//*!*/ positive = new ArrayList /*LinearConstraint!*/ (); ArrayList /*LinearConstraint!*//*!*/ eq = new ArrayList /*LinearConstraint!*/ (); foreach (LinearConstraint/*!*/ cc in constraints) { Contract.Assert(cc != null); Rational coeff = cc[dim]; if (coeff.IsZero) { LinearConstraint lc = cce.NonNull(cc.Clone()); if (!lc.IsConstant()) { lc.RemoveDimension(dim); final.Add(lc); } } else if (cc.Relation == LinearConstraint.ConstraintRelation.EQ) { eq.Add(cc); } else if (coeff.IsNegative) { negative.Add(cc); } else { System.Diagnostics.Debug.Assert(coeff.IsPositive); positive.Add(cc); } } if (eq.Count != 0) { LinearConstraint eqConstraint = (LinearConstraint/*!*/)cce.NonNull(eq[eq.Count - 1]); eq.RemoveAt(eq.Count - 1); Rational eqC = -eqConstraint[dim]; foreach (ArrayList /*LinearConstraint!*/ list in new ArrayList[] { eq, negative, positive }) { Contract.Assert(list != null); foreach (LinearConstraint/*!*/ lcX in list) { Contract.Assert(lcX != null); LinearConstraint lc = cce.NonNull(lcX.Clone()); lc.AddMultiple(lc[dim] / eqC, eqConstraint); System.Diagnostics.Debug.Assert(lc[dim].IsZero); if (!lc.IsConstant()) { lc.RemoveDimension(dim); final.Add(lc); } else { System.Diagnostics.Debug.Assert(lc.IsConstantSatisfiable()); } } } } else { // Consider all pairs of constraints with (negative,positive) coefficients of "dim". foreach (LinearConstraint/*!*/ cn in negative) { Contract.Assert(cn != null); Rational dn = -cn[dim]; System.Diagnostics.Debug.Assert(dn.IsNonNegative); foreach (LinearConstraint/*!*/ cp in positive) { Contract.Assert(cp != null); Rational dp = cp[dim]; LinearConstraint lc = new LinearConstraint(LinearConstraint.ConstraintRelation.LE); lc.AddMultiple(dn, cp); lc.AddMultiple(dp, cn); System.Diagnostics.Debug.Assert(lc[dim].IsZero); if (!lc.IsConstant()) { lc.RemoveDimension(dim); final.Add(lc); } else { System.Diagnostics.Debug.Assert(lc.IsConstantSatisfiable()); } } } } return final; } /// /// Initializes FrameVertices, FrameRays, FrameLines, and FrameDimensions, see /// Cousot and Halbwachs, section 3.4. Any previous values of these fields are /// ignored and overwritten. /// /// If the set of Constraints is unsatisfiable, then "this" is changed into Bottom. /// void GenerateFrameFromConstraints() { if (Constraints == null) { FrameVertices = null; FrameRays = null; FrameLines = null; FrameDimensions = new HashSet /*IVariable!*/ (); return; } // Step 1 (see Cousot and Halbwachs, section 3.4.3): create a Simplex Tableau. #if DEBUG_PRINT Console.WriteLine("DEBUG: --- GenerateFrameFromConstraint ---"); Console.WriteLine("Constraints:"); foreach (LinearConstraint cc in Constraints) { Console.WriteLine(" {0}", cc); } #endif SimplexTableau tableau = new SimplexTableau(Constraints); #if DEBUG_PRINT Console.WriteLine("Initial tableau:"); tableau.Dump(); #endif FrameDimensions = tableau.GetDimensions(); #if DEBUG_PRINT Console.WriteLine("Dimensions:"); foreach (object dim in FrameDimensions) { Console.Write(" {0}", dim); } Console.WriteLine(); #endif // Step 3 and 2: Put as many initial variables as possible into basis, then check if // we reached a feasible basis tableau.AddInitialVarsToBasis(); #if DEBUG_PRINT Console.WriteLine("Tableau after Step 3:"); tableau.Dump(); #endif if (!tableau.IsFeasibleBasis) { // The polyhedron is empty (according to Cousot and Halbwachs) ChangeIntoBottom(); return; } FrameVertices = new ArrayList /*FrameElement*/ (); FrameRays = new ArrayList /*FrameElement*/ (); FrameLines = new ArrayList /*FrameElement*/ (); if (FrameDimensions.Count == 0) { // top element return; } if (tableau.AllInitialVarsInBasis) { // All initial variables are in basis; there are no lines. #if DEBUG_PRINT Console.WriteLine("Tableau after Steps 2 and 3 (all initial variables in basis):"); tableau.Dump(); #endif } else { // There are lines #if DEBUG_PRINT Console.WriteLine("Tableau after Steps 2 and 3 (NOT all initial variables in basis--there are lines):"); tableau.Dump(); #endif // Step 4.2: Pick out the lines, then produce the tableau for a new polyhedron without those lines. ArrayList /*LinearConstraint*/ moreConstraints = cce.NonNull((ArrayList/*!*/ /*LinearConstraint*/)Constraints.Clone()); tableau.ProduceLines(FrameLines, moreConstraints); tableau = new SimplexTableau(moreConstraints); #if DEBUG_PRINT Console.WriteLine("Lines produced:"); foreach (FrameElement line in FrameLines) { Console.WriteLine(" {0}", line); } Console.WriteLine("The new list of constraints is:"); foreach (LinearConstraint c in moreConstraints) { Console.WriteLine(" {0}", c); } Console.WriteLine("Tableau after producing lines in Step 4.2:"); tableau.Dump(); #endif // Repeat step 3 for the new tableau. // Since the new tableau contains no lines, the following call should cause all initial // variables to be in basis (see step 4.2 in section 3.4.3 of Cousot and Halbwachs). tableau.AddInitialVarsToBasis(); System.Diagnostics.Debug.Assert(tableau.AllInitialVarsInBasis); System.Diagnostics.Debug.Assert(tableau.IsFeasibleBasis); // the new tableau represents a set of feasible constraints, so this basis should be found to be feasible #if DEBUG_PRINT Console.WriteLine("Tableau after all initial variables have been moved into basis:"); tableau.Dump(); #endif } // Step 4.1: One vertex has been found. Find all others, too. tableau.TraverseVertices(FrameVertices, FrameRays); #if DEBUG_PRINT Console.WriteLine("Tableau after vertex traversal:"); tableau.Dump(); #endif } class LambdaDimension : IVariable { readonly int id; static int count = 0; /// /// Return the name of the variable /// public string Name { get { Contract.Ensures(Contract.Result() != null); return this.ToString(); } } public LambdaDimension() { id = count; count++; } [Pure] public override string/*!*/ ToString() { Contract.Ensures(Contract.Result() != null); return "lambda" + id; } [Pure] public object DoVisit(ExprVisitor/*!*/ visitor) { Contract.Requires(visitor != null); return visitor.VisitVariable(this); } } /// /// 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. /// /// 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 } /// /// 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. /// /// 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 } /// /// 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. /// /// 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 } ISet /*IVariable!*//*!*/ GetDefinedDimensions() { Contract.Ensures(Contract.Result() != null); HashSet /*IVariable!*//*!*/ dims = new HashSet /*IVariable!*/ (); foreach (ArrayList p in new ArrayList[] { FrameVertices, FrameRays, FrameLines }) { if (p != null) { foreach (FrameElement/*!*/ element in p) { Contract.Assert(element != null); foreach (IVariable/*!*/ dim in element.GetDefinedDimensions()) { Contract.Assert(dim != null); dims.Add(dim); } } } } return dims; } // -------------------------------------------------------------------------------------------------------- // ------------------ Simplification routines ------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------- /// /// Uses the Constraints to simplify the frame. See section 3.4.4 of Cousot and Halbwachs. /// void SimplifyFrame() { Contract.Requires(this.Constraints != null); SimplificationStatus[]/*!*/ status; SimplifyFrameElements(cce.NonNull(FrameVertices), true, Constraints, out status); RemoveIrrelevantFrameElements(FrameVertices, status, null); SimplifyFrameElements(cce.NonNull(FrameRays), false, Constraints, out status); RemoveIrrelevantFrameElements(FrameRays, status, FrameLines); } enum SimplificationStatus { Irrelevant, Relevant, More }; /// /// For each i, sets status[i] to: ///
    ///
  • Irrelevant if ff[i] is irrelevant
  • ///
  • Relevant if ff[i] is irrelevant
  • ///
  • More if vertices is true and ray ff[i] can be replaced by a line ff[i]
  • ///
///
/// /// true if "ff" contains vertices; false if "ff" contains rays /// /// static void SimplifyFrameElements(ArrayList/*!*/ /*FrameElement*/ ff, bool vertices, ArrayList/*!*/ /*LinearConstraint*/ constraints, out SimplificationStatus[]/*!*/ status) { Contract.Requires(ff != null); Contract.Requires(constraints != null); Contract.Requires(Contract.ValueAtReturn(out status) != null); status = new SimplificationStatus[ff.Count]; bool[,] sat = new bool[ff.Count, constraints.Count]; for (int i = 0; i < ff.Count; i++) { FrameElement f = (FrameElement/*!*/)cce.NonNull(ff[i]); int cnt = 0; for (int c = 0; c < constraints.Count; c++) { LinearConstraint lc = (LinearConstraint/*!*/)cce.NonNull(constraints[c]); bool s = lc.IsSaturatedBy(f, vertices); if (s) { sat[i, c] = true; cnt++; } } if (!vertices && cnt == constraints.Count) { status[i] = SimplificationStatus.More; } else { status[i] = SimplificationStatus.Relevant; } } CheckPairSimplifications(sat, status); } /// /// Requires sat.GetLength(0) == status.Length. /// /// /// static void CheckPairSimplifications(bool[,]/*!*/ sat, SimplificationStatus[]/*!*/ status) { Contract.Requires(status != null); Contract.Requires(sat != null); Contract.Requires(sat.GetLength(0) == status.Length); int M = sat.GetLength(0); int N = sat.GetLength(1); for (int i = 0; i < M - 1; i++) { if (status[i] != SimplificationStatus.Relevant) { continue; } for (int j = i + 1; j < M; j++) { if (status[j] != SimplificationStatus.Relevant) { continue; } // check (sat[i,*] <= sat[j,*]) and (sat[i,*] >= sat[j,*]) int cmp = 0; // -1: (sat[i,*] <= sat[j,*]), 0: equal, 1: (sat[i,*] >= sat[j,*]) for (int c = 0; c < N; c++) { if (cmp < 0) { if (sat[i, c] && !sat[j, c]) { // incomparable goto NEXT_PAIR; } } else if (0 < cmp) { if (!sat[i, c] && sat[j, c]) { // incomparable goto NEXT_PAIR; } } else if (sat[i, c] != sat[j, c]) { if (!sat[i, c]) { cmp = -1; } else { cmp = 1; } } } if (cmp <= 0) { // sat[i,*] <= sat[j,*] holds, so mark i as irrelevant status[i] = SimplificationStatus.Irrelevant; goto NEXT_OUTER; } else { // sat[i,*] >= sat[j,*] holds, so mark j as irrelevant status[j] = SimplificationStatus.Irrelevant; } NEXT_PAIR: { } } NEXT_OUTER: { } } } static void RemoveIrrelevantFrameElements(ArrayList/*!*/ /*FrameElement*/ ff, SimplificationStatus[]/*!*/ status, /*maybe null*/ ArrayList /*FrameElement*/ lines) { Contract.Requires(ff != null); Contract.Requires(status != null); Contract.Requires(ff.Count == status.Length); for (int j = ff.Count - 1; 0 <= j; j--) { switch (status[j]) { case SimplificationStatus.Relevant: break; case SimplificationStatus.Irrelevant: #if DEBUG_PRINT Console.WriteLine("Removing irrelevant {0}: {1}", lines == null ? "vertex" : "ray", ff[j]); #endif ff.RemoveAt(j); break; case SimplificationStatus.More: System.Diagnostics.Debug.Assert(lines != null); FrameElement f = (FrameElement)ff[j]; #if DEBUG_PRINT Console.WriteLine("Changing ray into line: {0}", f); #endif ff.RemoveAt(j); Contract.Assert(lines != null); lines.Add(f); break; } } } /// /// Uses the frame to simplify Constraints. See section 3.3.1.2 of Cousot and Halbwachs. /// /// Note: This code does not necessarily eliminate all irrelevant equalities; Cousot and /// Halbwachs only claim that the technique eliminates all irrelevant inequalities. /// void SimplifyConstraints() { if (Constraints == null) { return; } Contract.Assume(this.FrameVertices != null); Contract.Assume(this.FrameRays != null); SimplificationStatus[] status = new SimplificationStatus[Constraints.Count]; /*readonly*/ int feCount = FrameVertices.Count + FrameRays.Count; // Create a table that keeps track of which constraints are satisfied by which vertices and rays bool[,] sat = new bool[Constraints.Count, FrameVertices.Count + FrameRays.Count]; for (int i = 0; i < Constraints.Count; i++) { status[i] = SimplificationStatus.Relevant; LinearConstraint lc = (LinearConstraint/*!*/)cce.NonNull(Constraints[i]); int cnt = 0; // number of vertices and rays that saturate lc for (int j = 0; j < FrameVertices.Count; j++) { FrameElement vertex = (FrameElement/*!*/)cce.NonNull(FrameVertices[j]); if (lc.IsSaturatedBy(vertex, true)) { sat[i, j] = true; cnt++; } } if (cnt == 0) { // no vertex saturates the constraint, so the constraint is irrelevant status[i] = SimplificationStatus.Irrelevant; continue; } for (int j = 0; j < FrameRays.Count; j++) { FrameElement ray = (FrameElement/*!*/)cce.NonNull(FrameRays[j]); if (lc.IsSaturatedBy(ray, false)) { sat[i, FrameVertices.Count + j] = true; cnt++; } } if (cnt == feCount) { status[i] = SimplificationStatus.More; } else { // Cousot and Halbwachs says that all equalities are found in the way we just tested. // If I understand that right, then we should not get here if the constraint is an // equality constraint. The following assertion tests my understanding. --KRML System.Diagnostics.Debug.Assert(lc.Relation == LinearConstraint.ConstraintRelation.LE); } } CheckPairSimplifications(sat, status); // Finally, make the changes to the list of constraints for (int i = Constraints.Count - 1; 0 <= i; i--) { switch (status[i]) { case SimplificationStatus.Relevant: break; case SimplificationStatus.Irrelevant: #if DEBUG_PRINT Console.WriteLine("Removing irrelevant constraint: {0}", Constraints[i]); #endif Constraints.RemoveAt(i); break; case SimplificationStatus.More: LinearConstraint lc = (LinearConstraint/*!*/)cce.NonNull(Constraints[i]); if (lc.Relation == LinearConstraint.ConstraintRelation.LE) { #if DEBUG_PRINT Console.WriteLine("Converting the following constraint into an equality: {0}", lc); #endif LinearConstraint lcEq = lc.ChangeRelation(LinearConstraint.ConstraintRelation.EQ); Constraints[i] = lcEq; } break; } } foreach (LinearConstraint/*!*/ lc in Constraints) { Contract.Assert(lc != null); lc.Normalize(); } } // -------------------------------------------------------------------------------------------------------- // ------------------ Cloning routines -------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------- public LinearConstraintSystem/*!*/ Clone() { Contract.Ensures(Contract.Result() != null); LinearConstraintSystem z = new LinearConstraintSystem(); z.FrameDimensions = (IMutableSet /*IVariable!*//*!*/)cce.NonNull(this.FrameDimensions.Clone()); if (this.Constraints != null) { z.Constraints = DeeperListCopy_LC(this.Constraints); z.FrameVertices = DeeperListCopy_FE(cce.NonNull(this.FrameVertices)); z.FrameRays = DeeperListCopy_FE(cce.NonNull(this.FrameRays)); z.FrameLines = DeeperListCopy_FE(cce.NonNull(this.FrameLines)); } else { System.Diagnostics.Debug.Assert(this.FrameVertices == null); System.Diagnostics.Debug.Assert(this.FrameRays == null); System.Diagnostics.Debug.Assert(this.FrameLines == null); // the constructor should already have set these fields of z to null System.Diagnostics.Debug.Assert(z.Constraints == null); System.Diagnostics.Debug.Assert(z.FrameVertices == null); System.Diagnostics.Debug.Assert(z.FrameRays == null); System.Diagnostics.Debug.Assert(z.FrameLines == null); } return z; } /// /// Clones "list" and the elements of "list". /// /// /// ArrayList /*LinearConstraint*/ DeeperListCopy_LC(ArrayList/*!*/ /*LinearConstraint*/ list) { Contract.Requires(list != null); ArrayList /*LinearConstraint*/ z = new ArrayList /*LinearConstraint*/ (list.Count); foreach (LinearConstraint/*!*/ lc in list) { Contract.Assert(lc != null); z.Add(lc.Clone()); } System.Diagnostics.Debug.Assert(z.Count == list.Count); return z; } /// /// Clones "list" and the elements of "list". /// /// /// ArrayList /*FrameElement*/ DeeperListCopy_FE(ArrayList/*!*/ /*FrameElement*/ list) { Contract.Requires(list != null); ArrayList /*FrameElement*/ z = new ArrayList /*FrameElement*/ (list.Count); foreach (FrameElement/*!*/ fe in list) { Contract.Assert(fe != null); z.Add(fe.Clone()); } System.Diagnostics.Debug.Assert(z.Count == list.Count); return z; } // -------------------------------------------------------------------------------------------------------- // ------------------ Debugging and unit test routines ---------------------------------------------------- // -------------------------------------------------------------------------------------------------------- public void Dump() { Console.WriteLine(" Constraints:"); if (Constraints == null) { Console.WriteLine(" "); } else { foreach (LinearConstraint cc in Constraints) { Console.WriteLine(" {0}", cc); } } Console.WriteLine(" FrameDimensions: {0}", FrameDimensions); Console.WriteLine(" FrameVerticies:"); if (FrameVertices == null) { Console.WriteLine(" "); } else { foreach (FrameElement fe in FrameVertices) { Console.WriteLine(" {0}", fe); } } Console.WriteLine(" FrameRays:"); if (FrameRays == null) { Console.WriteLine(" "); } else { foreach (FrameElement fe in FrameRays) { Console.WriteLine(" {0}", fe); } } Console.WriteLine(" FrameLines:"); if (FrameLines == null) { Console.WriteLine(" "); } else { foreach (FrameElement fe in FrameLines) { Console.WriteLine(" {0}", fe); } } } class TestVariable : IVariable { readonly string/*!*/ name; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(name != null); } public string/*!*/ Name { get { Contract.Ensures(Contract.Result() != null); return name; } } public TestVariable(string/*!*/ name) { Contract.Requires(name != null); this.name = name; } [Pure] public object DoVisit(ExprVisitor/*!*/ visitor) { Contract.Requires(visitor != null); return visitor.VisitVariable(this); } } 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(); } /// /// Tests the example in section 3.4.3 of Cousot and Halbwachs. /// public static void RunValidationB() { IVariable/*!*/ X = new TestVariable("X"); IVariable/*!*/ Y = new TestVariable("Y"); IVariable/*!*/ Z = new TestVariable("Z"); Contract.Assert(X != null); Contract.Assert(Y != null); Contract.Assert(Z != null); ArrayList /*LinearConstraint*/ cs = new ArrayList /*LinearConstraint*/ (); LinearConstraint c = new LinearConstraint(LinearConstraint.ConstraintRelation.LE); c.SetCoefficient(X, Rational.MINUS_ONE); c.SetCoefficient(Y, Rational.ONE); c.SetCoefficient(Z, Rational.MINUS_ONE); c.rhs = Rational.ZERO; cs.Add(c); c = new LinearConstraint(LinearConstraint.ConstraintRelation.LE); c.SetCoefficient(X, Rational.MINUS_ONE); c.rhs = Rational.MINUS_ONE; cs.Add(c); c = new LinearConstraint(LinearConstraint.ConstraintRelation.LE); c.SetCoefficient(X, Rational.MINUS_ONE); c.SetCoefficient(Y, Rational.MINUS_ONE); c.SetCoefficient(Z, Rational.ONE); c.rhs = Rational.ZERO; cs.Add(c); c = new LinearConstraint(LinearConstraint.ConstraintRelation.LE); c.SetCoefficient(Y, Rational.MINUS_ONE); c.SetCoefficient(Z, Rational.ONE); c.rhs = Rational.FromInt(3); cs.Add(c); LinearConstraintSystem lcs = new LinearConstraintSystem(cs); Console.WriteLine("==================== The final linear constraint system ===================="); lcs.Dump(); } 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 } } }