//----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //----------------------------------------------------------------------------- using System.Diagnostics.Contracts; namespace Microsoft.AbstractInterpretationFramework { using System; //using System.Compiler; using System.Collections; using Microsoft.Basetypes; using Set = Microsoft.Boogie.GSet; using IMutableSet = Microsoft.Boogie.GSet; using HashSet = Microsoft.Boogie.GSet; using ISet = Microsoft.Boogie.GSet; /// /// Represents a single linear constraint, coefficients are stored as Rationals. /// public class LinearConstraint { public enum ConstraintRelation { EQ, // equal LE, // less-than or equal } [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(coefficients != null); } public readonly ConstraintRelation Relation; internal Hashtable /*IVariable->Rational*//*!*/ coefficients = new Hashtable /*IVariable->Rational*/ (); internal Rational rhs; public LinearConstraint(ConstraintRelation rel) { Relation = rel; } [Pure] public override string/*!*/ ToString() { Contract.Ensures(Contract.Result() != null); string s = null; foreach (DictionaryEntry /*IVariable->Rational*/ entry in coefficients) { if (s == null) { s = ""; } else { s += " + "; } s += String.Format("{0}*{1}", entry.Value, entry.Key); } System.Diagnostics.Debug.Assert(s != null, "malformed LinearConstraint: no variables"); s += String.Format(" {0} {1}", Relation == ConstraintRelation.EQ ? "==" : "<=", rhs); return s; } #if DONT_KNOW_HOW_TO_TAKE_THE_TYPE_OF_AN_IVARIABLE_YET public bool IsOverIntegers { get { foreach (DictionaryEntry /*IVariable->Rational*/ entry in coefficients) { IVariable var = (IVariable)entry.Key; if ( ! var.TypedIdent.Type.IsInt) { return false; } } return true; } } #endif /// /// 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); IExpr leftSum = null; IExpr rightSum = null; foreach (DictionaryEntry /*object->Rational*/ entry in coefficients) { IVariable var = (IVariable)entry.Key; Rational coeff = (Rational)(cce.NonNull(entry.Value)); if (coeff.IsPositive) { leftSum = AddTerm(factory, leftSum, coeff, var); } else if (coeff.IsNegative) { rightSum = AddTerm(factory, rightSum, -coeff, var); } else { // ignore the term is coeff==0 } } if (leftSum == null && rightSum == null) { // there are no variables in this constraint if (Relation == ConstraintRelation.EQ ? rhs.IsZero : rhs.IsNonNegative) { return factory.True; } else { return factory.False; } } if (leftSum == null || (rightSum != null && rhs.IsNegative)) { // show the constant on the left side leftSum = AddTerm(factory, leftSum, -rhs, null); } else if (rightSum == null || rhs.IsPositive) { // show the constant on the right side rightSum = AddTerm(factory, rightSum, rhs, null); } Contract.Assert(leftSum != null); Contract.Assert(rightSum != null); return Relation == ConstraintRelation.EQ ? factory.Eq(leftSum, rightSum) : factory.AtMost(leftSum, rightSum); } /// /// Returns an expression that denotes sum + r*x. /// If sum==null, drops the "sum +". /// If x==null, drops the "*x". /// if x!=null and r==1, drops the "r*". /// /// /// /// /// static IExpr/*!*/ AddTerm(ILinearExprFactory/*!*/ factory, /*MayBeNull*/ IExpr sum, Rational r, /*MayBeNull*/ IVariable x) { Contract.Requires(factory != null); Contract.Ensures(Contract.Result() != null); IExpr/*!*/ product = factory.Term(r, x); Contract.Assert(product != null); if (sum == null) { return product; } else { return factory.Add(sum, product); } } public System.Collections.Generic.IEnumerable GetDefinedDimensionsGeneric() { Contract.Ensures(Contract.Result>() != null); foreach (IVariable/*!*/ dim in coefficients.Keys) { Contract.Assert(dim != null); yield return dim; } } public ISet /*IVariable!*//*!*/ GetDefinedDimensions() { Contract.Ensures(Contract.Result() != null); HashSet /*IVariable!*/ dims = new HashSet /*IVariable!*/ (coefficients.Count); int j = 0; foreach (IVariable/*!*/ dim in coefficients.Keys) { Contract.Assert(dim != null); dims.Add(dim); j++; } System.Diagnostics.Debug.Assert(j == coefficients.Count); return dims; } /// /// Returns true iff all of the coefficients in the constraint are 0. In that /// case, the constraint has the form 0 <= C for some constant C; hence, the /// constraint is either unsatisfiable or trivially satisfiable. /// /// public bool IsConstant() { foreach (Rational coeff in coefficients.Values) { if (coeff.IsNonZero) { return false; } } return true; } /// /// For an equality constraint, returns 0 == rhs. /// For an inequality constraint, returns 0 <= rhs. /// public bool IsConstantSatisfiable() { if (Relation == ConstraintRelation.EQ) { return rhs.IsZero; } else { return rhs.IsNonNegative; } } /// /// Returns 0 if "this" and "c" are not equivalent constraints. If "this" and "c" /// are equivalent constraints, the non-0 return value "m" satisfies "this == m*c". /// /// /// public Rational IsEquivalent(LinearConstraint/*!*/ c) { Contract.Requires(c != null); // "m" is the scale factor. If it is 0, it hasn't been used yet. If it // is non-0, it will remain that value throughout, and it then says that // for every dimension "d", "this[d] == m * c[d]". Rational m = Rational.ZERO; ArrayList /*IVariable*/ dd = new ArrayList /*IVariable*/ (); foreach (IVariable/*!*/ d in this.GetDefinedDimensions()) { Contract.Assert(d != null); if (!dd.Contains(d)) { dd.Add(d); } } foreach (IVariable/*!*/ d in c.GetDefinedDimensions()) { Contract.Assert(d != null); if (!dd.Contains(d)) { dd.Add(d); } } foreach (IVariable/*!*/ d in dd) { Contract.Assert(d != null); Rational a = this[d]; Rational b = c[d]; if (a.IsZero || b.IsZero) { if (a.IsNonZero || b.IsNonZero) { return Rational.ZERO; // not equivalent } } else if (m.IsZero) { m = a / b; } else if (a != m * b) { return Rational.ZERO; // not equivalent } } // we expect there to have been some non-zero coefficient, so "m" should have been used by now System.Diagnostics.Debug.Assert(m.IsNonZero); // finally, check the rhs if (this.rhs == m * c.rhs) { return m; // equivalent } else { return Rational.ZERO; // not equivalent } } /// /// Splits an equality constraint into two inequality constraints, the conjunction of /// which equals the equality constraint. Assumes "this" is a equality constraint. /// /// /// public void GenerateInequalityConstraints(out LinearConstraint a, out LinearConstraint b) { System.Diagnostics.Debug.Assert(this.Relation == ConstraintRelation.EQ); a = new LinearConstraint(ConstraintRelation.LE); a.coefficients = (Hashtable)this.coefficients.Clone(); a.rhs = this.rhs; b = new LinearConstraint(ConstraintRelation.LE); b.coefficients = new Hashtable /*IVariable->Rational*/ (); foreach (DictionaryEntry entry in this.coefficients) { b.coefficients[entry.Key] = -(Rational)(cce.NonNull(entry.Value)); } b.rhs = -this.rhs; } public void SetCoefficient(IVariable/*!*/ dimension, Rational coefficient) { Contract.Requires(dimension != null); coefficients[dimension] = coefficient; } /// /// Removes dimension "dim" from the constraint. Only dimensions with coefficient 0 can /// be removed. /// /// public void RemoveDimension(IVariable/*!*/ dim) { Contract.Requires(dim != null); object val = coefficients[dim]; if (val != null) { #if FIXED_SERIALIZER Contract.Assert(((Rational)val).IsZero); #endif coefficients.Remove(dim); } } /// /// The getter returns 0 if the dimension is not present. /// public Rational this[IVariable/*!*/ dimension] { get { Contract.Requires(dimension != null); object z = coefficients[dimension]; if (z == null) { return Rational.ZERO; } else { return (Rational)z; } } set { SetCoefficient(dimension, value); } } public LinearConstraint Rename(IVariable/*!*/ oldName, IVariable/*!*/ newName) { Contract.Requires(newName != null); Contract.Requires(oldName != null); object /*Rational*/ z = coefficients[oldName]; if (z == null) { return this; } else { System.Diagnostics.Debug.Assert(z is Rational); Hashtable /*IVariable->Rational*/ newCoeffs = (Hashtable/*!*/ /*IVariable->Rational*/)cce.NonNull(coefficients.Clone()); newCoeffs.Remove(oldName); newCoeffs.Add(newName, z); LinearConstraint lc = new LinearConstraint(this.Relation); lc.coefficients = newCoeffs; lc.rhs = this.rhs; return lc; } } public LinearConstraint Clone() { LinearConstraint z = new LinearConstraint(Relation); z.coefficients = (Hashtable /*IVariable->Rational*/)this.coefficients.Clone(); z.rhs = this.rhs; return z; } /// /// Returns a constraint like "this", but with the given relation "r". /// /// public LinearConstraint/*!*/ ChangeRelation(ConstraintRelation rel) { Contract.Ensures(Contract.Result() != null); if (Relation == rel) { return this; } else { LinearConstraint z = new LinearConstraint(rel); z.coefficients = (Hashtable)this.coefficients.Clone(); z.rhs = this.rhs; return z; } } /// /// Returns a constraint like "this", but, conceptually, with the inequality relation >=. /// /// public LinearConstraint/*!*/ ChangeRelationToAtLeast() { Contract.Ensures(Contract.Result() != null); LinearConstraint z = new LinearConstraint(ConstraintRelation.LE); foreach (DictionaryEntry /*IVariable->Rational*/ entry in this.coefficients) { z.coefficients.Add(entry.Key, -(Rational)(cce.NonNull(entry.Value))); } z.rhs = -this.rhs; return z; } /// /// 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. /// /// /// 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; } /// /// Determines whether or not a given vertex or ray saturates the constraint. /// /// /// true if "fe" is a vertex; false if "fe" is a ray /// 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; } /// /// Changes the current constraint A*X <= B into (A + m*aa)*X <= B + m*bb, /// where "cc" is the constraint aa*X <= bb. /// /// /// /// public void AddMultiple(Rational m, LinearConstraint/*!*/ cc) { Contract.Requires(cc != null); foreach (DictionaryEntry /*IVariable->Rational*/ entry in cc.coefficients) { IVariable dim = (IVariable)entry.Key; Rational d = m * (Rational)(cce.NonNull(entry.Value)); if (d.IsNonZero) { object prev = coefficients[dim]; if (prev == null) { coefficients[dim] = d; } else { coefficients[dim] = (Rational)prev + d; } } } rhs += m * cc.rhs; } /// /// Try to reduce the magnitude of the coefficients used. /// Has a side effect on the coefficients, but leaves the meaning of the linear constraint /// unchanged. /// public void Normalize() { // compute the gcd of the numerators and the gcd of the denominators Rational gcd = rhs; foreach (Rational r in coefficients.Values) { gcd = Rational.Gcd(gcd, r); } // Change all coefficients, to divide their numerators with gcdNum and to // divide their denominators with gcdDen. Hashtable /*IVariable->Rational*/ newCoefficients = new Hashtable /*IVariable->Rational*/ (coefficients.Count); foreach (DictionaryEntry /*IVarianble->Rational*/ e in coefficients) { Rational r = (Rational)(cce.NonNull(e.Value)); if (r.IsNonZero) { newCoefficients.Add(e.Key, Rational.FromBignums(r.Numerator / gcd.Numerator, r.Denominator / gcd.Denominator)); } else { newCoefficients.Add(e.Key, r); } } coefficients = newCoefficients; rhs = rhs.IsNonZero ? Rational.FromBignums(rhs.Numerator / gcd.Numerator, rhs.Denominator / gcd.Denominator) : rhs; } } /// /// Represents a frame element (vector of dimension/value tuples). Used only /// internally in class LinearConstraintSystem and its communication with class /// LinearConstraint. /// public class FrameElement { [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(terms != null); } Hashtable /*IVariable->Rational*//*!*/ terms = new Hashtable /*IVariable->Rational*/ (); /// /// Constructs an empty FrameElement. To add dimensions, call AddCoordinate after construction. /// public FrameElement() { } /// /// This method is to be thought of as being part of the FrameElement object's construction process. /// Assumes "dimension" is not already in FrameElement. /// /// /// public void AddCoordinate(IVariable/*!*/ dimension, Rational value) { Contract.Requires(dimension != null); terms.Add(dimension, value); } [Pure] public override string/*!*/ ToString() { Contract.Ensures(Contract.Result() != null); string s = null; foreach (DictionaryEntry item in terms) { if (s == null) { s = "("; } else { s += ", "; } s += String.Format("<{0},{1}>", item.Key, (Rational)(cce.NonNull(item.Value))); } if (s == null) { s = "("; } return s + ")"; } public IMutableSet /*IVariable!*//*!*/ GetDefinedDimensions() { Contract.Ensures(Contract.Result() != null); HashSet /*IVariable!*//*!*/ dims = new HashSet /*IVariable!*/ (terms.Count); foreach (IVariable/*!*/ dim in terms.Keys) { Contract.Assert(dim != null); dims.Add(dim); } System.Diagnostics.Debug.Assert(dims.Count == terms.Count); return dims; } /// /// The getter returns the value at the given dimension, or 0 if that dimension is not defined. /// public Rational this[IVariable/*!*/ dimension] { get { //Contract.Ensures(Contract.Result() != null); object z = terms[dimension]; if (z == null) { return Rational.ZERO; } else { return (Rational)z; } } set { terms[dimension] = value; } } 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; } } }