// // IntervalInference.cs // // Authors: // Alexander Chebaturkin (chebaturkin@gmail.com) // // Copyright (C) 2012 Alexander Chebaturkin // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using Mono.CodeContracts.Static.DataStructures; using Mono.CodeContracts.Static.Lattices; namespace Mono.CodeContracts.Static.Analysis.Numerical { static class IntervalInference { public static class ConstraintsFor { public static IDictionary> GreaterEqualThanZero (TExpr expr, IExpressionDecoder decoder, TEnv env) where TEnv : IIntervalEnvironment where TVar : IEquatable where TInterval : IntervalBase { var result = new Dictionary> (); var variable = decoder.UnderlyingVariable (expr); AddToResult (result, variable, env.Eval (expr).Meet (env.Context.Positive)); if (!decoder.IsVariable (expr)) { Polynomial zeroPoly; // poly(0) if (!Polynomial.TryToPolynomial (new[] {Monomial.From (Rational.Zero)}, out zeroPoly)) throw new AbstractInterpretationException ( "It can never be the case that the conversion of a list of monomials into a polynomial fails."); Polynomial exprPoly; // poly(expr) Polynomial fullPoly; // '0 <= poly(expr)' polynome if (Polynomial.TryBuildFrom (expr, decoder, out exprPoly) && Polynomial.TryToPolynomial (ExpressionOperator.LessEqualThan, zeroPoly, exprPoly, out fullPoly) && fullPoly.IsIntervalForm) { var k = fullPoly.Left[0].Coeff; // k != 0 TVar x; fullPoly.Left[0].IsSingleVariable (out x); Rational constraint; if (Rational.TryDivide (fullPoly.Right[0].Coeff, k, out constraint)) { TInterval interval; if (k > 0L) // +x <= constraint interval = env.Eval (x).Meet (env.Context.For (Rational.MinusInfinity, constraint)); else // -x <= -constraint ==> x >= constraint interval = env.Eval (x).Meet (env.Context.For (constraint, Rational.PlusInfinity)); AddToResult (result, x, interval); } } } return result; } public static IDictionary> LessEqualThan (TExpr left, TExpr right, IExpressionDecoder decoder, TEnv env, out bool isBottom) where TEnv : IIntervalEnvironment where TVar : IEquatable where TInterval : IntervalBase { isBottom = false; var result = new Dictionary> (); if (IsFloat (left, decoder) || IsFloat (right, decoder)) return result; var leftIntv = env.Eval (left); var rightIntv = env.Eval (right); var leftVar = decoder.UnderlyingVariable (left); var rightVar = decoder.UnderlyingVariable (right); TInterval refinedIntv; if (TryRefineLessEqualThan (leftIntv, rightVar, env, out refinedIntv)) AddToResult (result, rightVar, refinedIntv); if (TryRefineLeftLessEqualThanK (leftVar, rightIntv, env, out refinedIntv)) AddToResult (result, leftVar, refinedIntv); Polynomial poly; Polynomial leftPoly; Polynomial rightPoly; if (Polynomial.TryBuildFrom (left, decoder, out leftPoly) && Polynomial.TryBuildFrom (right, decoder, out rightPoly) && Polynomial.TryToPolynomial (ExpressionOperator.LessEqualThan, leftPoly, rightPoly, out poly) && poly.IsLinear) { if (poly.Left.Length == 1) return TestTrueLessEqualThan_AxLeqK (poly, env, result, out isBottom); if (poly.Left.Length == 2) return TestTrueLessEqualThan_AxByLeqK (poly, env, result, out isBottom); } return result; } public static IDictionary> LessThan (TExpr left, TExpr right, IExpressionDecoder decoder, TEnv env, out bool isBottom) where TEnv : IIntervalEnvironment where TVar : IEquatable where TInterval : IntervalBase { isBottom = false; var result = new Dictionary> (); var leftIntv = env.Eval (left); var rightIntv = env.Eval (right); var rightVar = decoder.UnderlyingVariable (right); var successor = IsFloat (left, decoder) || IsFloat (right, decoder) ? Rational.Zero : Rational.One; TInterval refinedIntv; if (TryRefineKLessThanRight (leftIntv, rightVar, successor, env, out refinedIntv) && !refinedIntv.IsSinglePoint) AddToResult (result, rightVar, refinedIntv); if (successor.IsZero) return result; var leftVar = decoder.UnderlyingVariable (left); if (TryRefineLessThan (leftVar, rightIntv, env, out refinedIntv) && !refinedIntv.IsSinglePoint) AddToResult (result, leftVar, refinedIntv); Polynomial poly; Polynomial leftPoly; Polynomial rightPoly; if (Polynomial.TryBuildFrom (left, decoder, out leftPoly) && Polynomial.TryBuildFrom (right, decoder, out rightPoly) && Polynomial.TryToPolynomial (ExpressionOperator.LessThan, leftPoly, rightPoly, out poly) && poly.IsLinear) { if (poly.Left.Length == 1) return TestTrueLessEqualThan_AxLtK (poly, env, result, out isBottom); if (poly.Left.Length == 2) return TestTrueLessEqualThan_AxByLtK (poly, env, result, out isBottom); } return result; } /// /// Get interval for 'left' in inequation 'left <= k'. /// public static bool TryRefineLeftLessEqualThanK (TVar left, TInterval k, TEnv env, out TInterval refined) where TEnv : IIntervalEnvironment where TInterval : IntervalBase { if (!k.IsNormal ()) return false.Without (out refined); var interval = env.Context.For (Rational.MinusInfinity, k.UpperBound); TInterval leftIntv; if (env.TryGetValue (left, out leftIntv)) interval = interval.Meet (leftIntv); return true.With (interval, out refined); } /// /// Get interval for 'left' in inequation 'left < k'. /// public static bool TryRefineLessThan (TVar left, TInterval k, TEnv env, out TInterval refined) where TEnv : IIntervalEnvironment where TInterval : IntervalBase { if (!k.IsNormal ()) return false.Without (out refined); var interval = env.Context.For (Rational.MinusInfinity, k.UpperBound.IsInteger ? k.UpperBound - 1L : k.UpperBound); TInterval leftIntv; if (env.TryGetValue (left, out leftIntv)) interval = interval.Meet (leftIntv); return true.With (interval, out refined); } /// /// Get interval for 'right' in inequation 'k <= right'. /// public static bool TryRefineLessEqualThan (TInterval k, TVar right, TEnv env, out TInterval refined) where TEnv : IIntervalEnvironment where TInterval : IntervalBase { if (!k.IsNormal ()) return false.Without (out refined); var interval = env.Context.RightOpen (k.LowerBound); TInterval rightIntv; if (env.TryGetValue (right, out rightIntv)) interval = interval.Meet (rightIntv); return true.With (interval, out refined); } /// /// Get interval for 'right' in inequation 'k < right'. /// public static bool TryRefineKLessThanRight (TInterval k, TVar right, Rational successor, TEnv env, out TInterval refined) where TEnv : IIntervalEnvironment where TInterval : IntervalBase { if (!k.IsNormal ()) return false.Without (out refined); // [k, +oo] or (k, +oo] var interval = env.Context.For (k.LowerBound.IsInteger ? k.LowerBound + successor : k.LowerBound, Rational.PlusInfinity); TInterval rightIntv; if (env.TryGetValue (right, out rightIntv)) interval = interval.Meet (rightIntv); return true.With (interval, out refined); } public static void NotEqual (TExpr left, TExpr right, IExpressionDecoder decoder, TEnv env, out InferenceResult resultLeft, out InferenceResult resultRight) where TInterval : IntervalBase where TEnv : IIntervalEnvironment where TVar : IEquatable { resultLeft = InferenceResult.Empty; resultRight = InferenceResult.Empty; var leftIntv = env.Eval (left); var rightIntv = env.Eval (right); var leftVar = decoder.UnderlyingVariable (left); var rightVar = decoder.UnderlyingVariable (right); var successor = IsFloat (left, decoder) || IsFloat (right, decoder) ? Rational.Zero : Rational.One; // l != r <==> l < r && r < l LessThanRefinement (successor, env, leftIntv, rightIntv, leftVar, rightVar, ref resultLeft); LessThanRefinement (successor, env, rightIntv, leftIntv, rightVar, leftVar, ref resultRight); } static void LessThanRefinement (Rational successor, TEnv env, TInterval leftIntv, TInterval rightIntv, TVar leftVar, TVar rightVar, ref InferenceResult result) where TEnv : IIntervalEnvironment where TInterval : IntervalBase where TVar : IEquatable { TInterval refined; if (TryRefineKLessThanRight (leftIntv, rightVar, successor, env, out refined)) result = result.AddConstraintFor (rightVar, refined); if (TryRefineLessThan (leftVar, rightIntv, env, out refined)) result = result.AddConstraintFor (leftVar, refined); } static IDictionary> TestTrueLessEqualThan_AxByLeqK (Polynomial poly, TEnv env, IDictionary> result, out bool isBottom) where TEnv : IIntervalEnvironment where TVar : IEquatable where TInterval : IntervalBase { // ax + by <= k var ax = poly.Left[0]; var by = poly.Left[1]; TVar x, y; ax.IsSingleVariable (out x); by.IsSingleVariable (out y); var k = poly.Right[0].Coeff; var a = ax.Coeff; var b = by.Coeff; var aInterval = env.Context.For (a); var bInterval = env.Context.For (b); var kInterval = env.Context.For (k); var xInterval = env.Eval (x); var yInterval = env.Eval (y); IntervalContextBase ctx = env.Context; // x <= (k - (b * y)) / a; var boundingInterval = ctx.Div (ctx.Sub (kInterval, ctx.Mul (bInterval, yInterval)), aInterval); Func upperBounded = (i) => ctx.For (Rational.MinusInfinity, i); Func lowerBounded = (i) => ctx.For (i, Rational.PlusInfinity); if (BoundVariable (ctx, a, xInterval, boundingInterval, x, result, out isBottom, upperBounded, lowerBounded)) return result; // y <= (k - (a * x)) / b; boundingInterval = ctx.Div (ctx.Sub (kInterval, ctx.Mul (aInterval, xInterval)), bInterval); if (BoundVariable (ctx, b, yInterval, boundingInterval, y, result, out isBottom, upperBounded, lowerBounded)) return result; return result; } static IDictionary> TestTrueLessEqualThan_AxByLtK (Polynomial poly, TEnv env, IDictionary> result, out bool isBottom) where TEnv : IIntervalEnvironment where TVar : IEquatable where TInterval : IntervalBase { // ax + by <= k var ax = poly.Left[0]; var by = poly.Left[1]; TVar x, y; ax.IsSingleVariable (out x); by.IsSingleVariable (out y); var k = poly.Right[0].Coeff; var a = ax.Coeff; var b = by.Coeff; var aInterval = env.Context.For (a); var bInterval = env.Context.For (b); var kInterval = env.Context.For (k); var xInterval = env.Eval (x); var yInterval = env.Eval (y); IntervalContextBase ctx = env.Context; Func upperBounded = (i) => ctx.For (Rational.MinusInfinity, !i.IsInteger ? i.PreviousInt32 : i - 1L); Func lowerBounded = (i) => ctx.For (!i.IsInteger ? i.NextInt32 : i + 1L, Rational.PlusInfinity); // x <= (k - (b * y)) / a; var boundingInterval = ctx.Div (ctx.Sub (kInterval, ctx.Mul (bInterval, yInterval)), aInterval); if (BoundVariable (ctx, a, xInterval, boundingInterval, x, result, out isBottom, upperBounded, lowerBounded)) return result; // y <= (k - (a * x)) / b; boundingInterval = ctx.Div (ctx.Sub (kInterval, ctx.Mul (aInterval, xInterval)), bInterval); if (BoundVariable (ctx, b, yInterval, boundingInterval, y, result, out isBottom, upperBounded, lowerBounded)) return result; return result; } /// /// Get constraints for variables from polynome in form 'a*x <= k' /// /// Polynome in canonical form. Two monomes involved. static IDictionary> TestTrueLessEqualThan_AxLeqK (Polynomial poly, TEnv env, IDictionary> result, out bool isBottom) where TEnv : IIntervalEnvironment where TVar : IEquatable where TInterval : IntervalBase { isBottom = false; var ax = poly.Left[0]; var k = poly.Right[1]; if (ax.IsConstant) { if (ax.Coeff > k.Coeff) { isBottom = true; return result; } } else { TVar x; ax.IsSingleVariable (out x); Rational div; if (Rational.TryDivide (k.Coeff, ax.Coeff, out div)) { var intv = env.Eval (x); var refined = ax.Coeff.Sign < 1 ? intv.Meet (env.Context.For (div, Rational.PlusInfinity)) : intv.Meet (env.Context.For (Rational.MinusInfinity, div)); AddToResult (result, x, refined); } } return result; } /// /// Get constraints for variables from polynome in form 'a*x <= k' /// /// Polynome in canonical form. Two monomes involved. static IDictionary> TestTrueLessEqualThan_AxLtK (Polynomial poly, TEnv env, IDictionary> result, out bool isBottom) where TEnv : IIntervalEnvironment where TVar : IEquatable where TInterval : IntervalBase { isBottom = false; var ax = poly.Left[0]; var k = poly.Right[1]; if (ax.IsConstant) { if (ax.Coeff >= k.Coeff) { isBottom = true; return result; } } else { TVar x; ax.IsSingleVariable (out x); Rational div; if (Rational.TryDivide (k.Coeff, ax.Coeff, out div)) { var intv = env.Eval (x); var boundByDivPlus = !div.IsInteger ? env.Context.For (div.NextInt32, Rational.PlusInfinity) : env.Context.For (div + 1L, Rational.PlusInfinity); var boundByDivMinus = !div.IsInteger ? env.Context.For (Rational.MinusInfinity, div.NextInt32 - 1L) : env.Context.For (Rational.MinusInfinity, div - 1L); var refined = intv.Meet (ax.Coeff.Sign < 1 ? boundByDivPlus : boundByDivMinus); if (refined.IsBottom) { isBottom = true; return result; } AddToResult (result, x, refined); } } return result; } static bool BoundVariable (IntervalContextBase ctx, Rational a, TInterval xIntervalOld, TInterval boundingInterval, TVar x, IDictionary> result, out bool isBottom, Func upperBounded, Func lowerBounded) where TVar : IEquatable where TInterval : IntervalBase { isBottom = false; if (a.IsZero) { TInterval boundingForVariable; if (a.Sign > 0L) boundingForVariable = upperBounded (boundingInterval.UpperBound); else boundingForVariable = lowerBounded (boundingInterval.LowerBound); var refined = xIntervalOld.Meet (boundingForVariable); if (refined.IsBottom) { isBottom = true; return true; } AddToResult (result, x, refined); } return false; } static bool IsFloat (TExpr expr, IExpressionDecoder decoder) { if (decoder == null) return false; var type = decoder.TypeOf (expr); return type == ExpressionType.Float32 || type == ExpressionType.Float64; } static void AddToResult (IDictionary> result, TVar variable, TInterval intv) where TVar : IEquatable where TInterval : IntervalBase { Sequence value; result.TryGetValue (variable, out value); result[variable] = value.Cons (intv); } } public struct InferenceResult where TInterval : IAbstractDomain where TVar : IEquatable { public static readonly InferenceResult Empty = new InferenceResult (false, ImmutableMap>.Empty); public readonly bool IsBottom; public readonly IImmutableMap> Constraints; InferenceResult (bool isbottom, IImmutableMap> map) { Constraints = map; IsBottom = isbottom; } public InferenceResult AddConstraintFor (TVar rightVar, TInterval refined) { Sequence seq; Constraints.TryGetValue (rightVar, out seq); return new InferenceResult (IsBottom, Constraints.Add (rightVar, seq.Cons (refined))); } public InferenceResult Join (InferenceResult that) { if (IsBottom) return that; if (that.IsBottom) return this; var result = Empty; foreach (var var in Constraints.Keys) { var leftContraints = Constraints[var]; if (leftContraints == null) continue; // tops are not included Sequence rightConstraints; if (that.Constraints.TryGetValue (var, out rightConstraints) && rightConstraints != null) { var intv = leftContraints.Head.Join (rightConstraints.Head); if (!intv.IsTop) result.AddConstraintFor (var, intv); } } return result; } } } }