2 // IntervalInference.cs
5 // Alexander Chebaturkin (chebaturkin@gmail.com)
7 // Copyright (C) 2012 Alexander Chebaturkin
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections.Generic;
32 using Mono.CodeContracts.Static.DataStructures;
33 using Mono.CodeContracts.Static.Lattices;
35 namespace Mono.CodeContracts.Static.Analysis.Numerical {
36 static class IntervalInference {
37 public static class ConstraintsFor {
38 public static IDictionary<TVar, Sequence<TInterval>> GreaterEqualThanZero<TEnv, TVar, TExpr, TInterval>
39 (TExpr expr, IExpressionDecoder<TVar, TExpr> decoder, TEnv env)
40 where TEnv : IIntervalEnvironment<TVar, TExpr, TInterval, Rational>
41 where TVar : IEquatable<TVar>
42 where TInterval : IntervalBase<TInterval, Rational>
44 var result = new Dictionary<TVar, Sequence<TInterval>> ();
45 var variable = decoder.UnderlyingVariable (expr);
47 AddToResult (result, variable, env.Eval (expr).Meet (env.Context.Positive));
49 if (!decoder.IsVariable (expr)) {
50 Polynomial<TVar, TExpr> zeroPoly; // poly(0)
51 if (!Polynomial<TVar, TExpr>.TryToPolynomial (new[] {Monomial<TVar>.From (Rational.Zero)}, out zeroPoly))
52 throw new AbstractInterpretationException (
53 "It can never be the case that the conversion of a list of monomials into a polynomial fails.");
55 Polynomial<TVar, TExpr> exprPoly; // poly(expr)
56 Polynomial<TVar, TExpr> fullPoly; // '0 <= poly(expr)' polynome
57 if (Polynomial<TVar, TExpr>.TryBuildFrom (expr, decoder, out exprPoly) &&
58 Polynomial<TVar, TExpr>.TryToPolynomial (ExpressionOperator.LessEqualThan, zeroPoly, exprPoly, out fullPoly) &&
59 fullPoly.IsIntervalForm) {
60 var k = fullPoly.Left[0].Coeff; // k != 0
62 fullPoly.Left[0].IsSingleVariable (out x);
65 if (Rational.TryDivide (fullPoly.Right[0].Coeff, k, out constraint)) {
67 if (k > 0L) // +x <= constraint
68 interval = env.Eval (x).Meet (env.Context.For (Rational.MinusInfinity, constraint));
69 else // -x <= -constraint ==> x >= constraint
70 interval = env.Eval (x).Meet (env.Context.For (constraint, Rational.PlusInfinity));
72 AddToResult (result, x, interval);
79 public static IDictionary<TVar, Sequence<TInterval>> LessEqualThan<TEnv, TVar, TExpr, TInterval>
80 (TExpr left, TExpr right, IExpressionDecoder<TVar, TExpr> decoder, TEnv env, out bool isBottom)
81 where TEnv : IIntervalEnvironment<TVar, TExpr, TInterval, Rational>
82 where TVar : IEquatable<TVar>
83 where TInterval : IntervalBase<TInterval, Rational>
86 var result = new Dictionary<TVar, Sequence<TInterval>> ();
88 if (IsFloat (left, decoder) || IsFloat (right, decoder))
91 var leftIntv = env.Eval (left);
92 var rightIntv = env.Eval (right);
94 var leftVar = decoder.UnderlyingVariable (left);
95 var rightVar = decoder.UnderlyingVariable (right);
97 TInterval refinedIntv;
98 if (TryRefineLessEqualThan<TEnv, TVar, TExpr, TInterval> (leftIntv, rightVar, env, out refinedIntv))
99 AddToResult (result, rightVar, refinedIntv);
101 if (TryRefineLeftLessEqualThanK<TEnv, TVar, TExpr, TInterval> (leftVar, rightIntv, env, out refinedIntv))
102 AddToResult (result, leftVar, refinedIntv);
104 Polynomial<TVar, TExpr> poly;
105 Polynomial<TVar, TExpr> leftPoly;
106 Polynomial<TVar, TExpr> rightPoly;
108 if (Polynomial<TVar, TExpr>.TryBuildFrom (left, decoder, out leftPoly) &&
109 Polynomial<TVar, TExpr>.TryBuildFrom (right, decoder, out rightPoly) &&
110 Polynomial<TVar, TExpr>.TryToPolynomial (ExpressionOperator.LessEqualThan, leftPoly, rightPoly, out poly) &&
112 if (poly.Left.Length == 1)
113 return TestTrueLessEqualThan_AxLeqK (poly, env, result, out isBottom);
114 if (poly.Left.Length == 2)
115 return TestTrueLessEqualThan_AxByLeqK (poly, env, result, out isBottom);
121 public static IDictionary<TVar, Sequence<TInterval>> LessThan<TEnv, TVar, TExpr, TInterval>
122 (TExpr left, TExpr right, IExpressionDecoder<TVar, TExpr> decoder, TEnv env, out bool isBottom)
123 where TEnv : IIntervalEnvironment<TVar, TExpr, TInterval, Rational>
124 where TVar : IEquatable<TVar>
125 where TInterval : IntervalBase<TInterval, Rational>
128 var result = new Dictionary<TVar, Sequence<TInterval>> ();
130 var leftIntv = env.Eval (left);
131 var rightIntv = env.Eval (right);
133 var rightVar = decoder.UnderlyingVariable (right);
134 var successor = IsFloat (left, decoder) || IsFloat (right, decoder) ? Rational.Zero : Rational.One;
136 TInterval refinedIntv;
137 if (TryRefineKLessThanRight<TEnv, TVar, TExpr, TInterval> (leftIntv, rightVar, successor, env, out refinedIntv) && !refinedIntv.IsSinglePoint)
138 AddToResult (result, rightVar, refinedIntv);
140 if (successor.IsZero)
143 var leftVar = decoder.UnderlyingVariable (left);
144 if (TryRefineLessThan<TEnv, TVar, TExpr, TInterval> (leftVar, rightIntv, env, out refinedIntv) && !refinedIntv.IsSinglePoint)
145 AddToResult (result, leftVar, refinedIntv);
147 Polynomial<TVar, TExpr> poly;
148 Polynomial<TVar, TExpr> leftPoly;
149 Polynomial<TVar, TExpr> rightPoly;
151 if (Polynomial<TVar, TExpr>.TryBuildFrom (left, decoder, out leftPoly) &&
152 Polynomial<TVar, TExpr>.TryBuildFrom (right, decoder, out rightPoly) &&
153 Polynomial<TVar, TExpr>.TryToPolynomial (ExpressionOperator.LessThan, leftPoly, rightPoly, out poly) &&
155 if (poly.Left.Length == 1)
156 return TestTrueLessEqualThan_AxLtK (poly, env, result, out isBottom);
157 if (poly.Left.Length == 2)
158 return TestTrueLessEqualThan_AxByLtK (poly, env, result, out isBottom);
165 /// Get interval for 'left' in inequation 'left <= k'.
167 public static bool TryRefineLeftLessEqualThanK<TEnv, TVar, TExpr, TInterval> (TVar left, TInterval k, TEnv env, out TInterval refined)
168 where TEnv : IIntervalEnvironment<TVar, TExpr, TInterval, Rational>
169 where TInterval : IntervalBase<TInterval, Rational>
172 return false.Without (out refined);
174 var interval = env.Context.For (Rational.MinusInfinity, k.UpperBound);
177 if (env.TryGetValue (left, out leftIntv))
178 interval = interval.Meet (leftIntv);
180 return true.With (interval, out refined);
184 /// Get interval for 'left' in inequation 'left < k'.
186 public static bool TryRefineLessThan<TEnv, TVar, TExpr, TInterval> (TVar left, TInterval k, TEnv env, out TInterval refined)
187 where TEnv : IIntervalEnvironment<TVar, TExpr, TInterval, Rational>
188 where TInterval : IntervalBase<TInterval, Rational>
191 return false.Without (out refined);
193 var interval = env.Context.For (Rational.MinusInfinity, k.UpperBound.IsInteger ? k.UpperBound - 1L : k.UpperBound);
196 if (env.TryGetValue (left, out leftIntv))
197 interval = interval.Meet (leftIntv);
199 return true.With (interval, out refined);
203 /// Get interval for 'right' in inequation 'k <= right'.
205 public static bool TryRefineLessEqualThan<TEnv, TVar, TExpr, TInterval> (TInterval k, TVar right, TEnv env, out TInterval refined)
206 where TEnv : IIntervalEnvironment<TVar, TExpr, TInterval, Rational>
207 where TInterval : IntervalBase<TInterval, Rational>
210 return false.Without (out refined);
212 var interval = env.Context.RightOpen (k.LowerBound);
215 if (env.TryGetValue (right, out rightIntv))
216 interval = interval.Meet (rightIntv);
218 return true.With (interval, out refined);
222 /// Get interval for 'right' in inequation 'k < right'.
224 public static bool TryRefineKLessThanRight<TEnv, TVar, TExpr, TInterval> (TInterval k, TVar right, Rational successor, TEnv env, out TInterval refined)
225 where TEnv : IIntervalEnvironment<TVar, TExpr, TInterval, Rational>
226 where TInterval : IntervalBase<TInterval, Rational>
229 return false.Without (out refined);
231 // [k, +oo] or (k, +oo]
232 var interval = env.Context.For (k.LowerBound.IsInteger ? k.LowerBound + successor : k.LowerBound, Rational.PlusInfinity);
235 if (env.TryGetValue (right, out rightIntv))
236 interval = interval.Meet (rightIntv);
238 return true.With (interval, out refined);
241 public static void NotEqual<TEnv, TVar, TExpr, TInterval>
242 (TExpr left, TExpr right, IExpressionDecoder<TVar, TExpr> decoder, TEnv env, out InferenceResult<TVar, TInterval> resultLeft,
243 out InferenceResult<TVar, TInterval> resultRight) where TInterval : IntervalBase<TInterval, Rational>
244 where TEnv : IIntervalEnvironment<TVar, TExpr, TInterval, Rational>
245 where TVar : IEquatable<TVar>
247 resultLeft = InferenceResult<TVar, TInterval>.Empty;
248 resultRight = InferenceResult<TVar, TInterval>.Empty;
250 var leftIntv = env.Eval (left);
251 var rightIntv = env.Eval (right);
253 var leftVar = decoder.UnderlyingVariable (left);
254 var rightVar = decoder.UnderlyingVariable (right);
256 var successor = IsFloat (left, decoder) || IsFloat (right, decoder) ? Rational.Zero : Rational.One;
258 // l != r <==> l < r && r < l
259 LessThanRefinement<TEnv, TVar, TExpr, TInterval> (successor, env, leftIntv, rightIntv, leftVar, rightVar, ref resultLeft);
260 LessThanRefinement<TEnv, TVar, TExpr, TInterval> (successor, env, rightIntv, leftIntv, rightVar, leftVar, ref resultRight);
263 static void LessThanRefinement<TEnv, TVar, TExpr, TInterval>
264 (Rational successor, TEnv env,
265 TInterval leftIntv, TInterval rightIntv,
266 TVar leftVar, TVar rightVar,
267 ref InferenceResult<TVar, TInterval> result)
268 where TEnv : IIntervalEnvironment<TVar, TExpr, TInterval, Rational>
269 where TInterval : IntervalBase<TInterval, Rational>
270 where TVar : IEquatable<TVar>
273 if (TryRefineKLessThanRight<TEnv, TVar, TExpr, TInterval> (leftIntv, rightVar, successor, env, out refined))
274 result = result.AddConstraintFor (rightVar, refined);
275 if (TryRefineLessThan<TEnv, TVar, TExpr, TInterval> (leftVar, rightIntv, env, out refined))
276 result = result.AddConstraintFor (leftVar, refined);
279 static IDictionary<TVar, Sequence<TInterval>> TestTrueLessEqualThan_AxByLeqK<TEnv, TVar, TExpr, TInterval>
280 (Polynomial<TVar, TExpr> poly, TEnv env, IDictionary<TVar, Sequence<TInterval>> result, out bool isBottom)
281 where TEnv : IIntervalEnvironment<TVar, TExpr, TInterval, Rational>
282 where TVar : IEquatable<TVar>
283 where TInterval : IntervalBase<TInterval, Rational>
286 var ax = poly.Left[0];
287 var by = poly.Left[1];
290 ax.IsSingleVariable (out x);
291 by.IsSingleVariable (out y);
293 var k = poly.Right[0].Coeff;
297 var aInterval = env.Context.For (a);
298 var bInterval = env.Context.For (b);
299 var kInterval = env.Context.For (k);
301 var xInterval = env.Eval (x);
302 var yInterval = env.Eval (y);
304 IntervalContextBase<TInterval, Rational> ctx = env.Context;
306 // x <= (k - (b * y)) / a;
307 var boundingInterval = ctx.Div (ctx.Sub (kInterval, ctx.Mul (bInterval, yInterval)), aInterval);
308 Func<Rational, TInterval> upperBounded = (i) => ctx.For (Rational.MinusInfinity, i);
309 Func<Rational, TInterval> lowerBounded = (i) => ctx.For (i, Rational.PlusInfinity);
311 if (BoundVariable (ctx, a, xInterval, boundingInterval, x, result, out isBottom, upperBounded, lowerBounded))
314 // y <= (k - (a * x)) / b;
315 boundingInterval = ctx.Div (ctx.Sub (kInterval, ctx.Mul (aInterval, xInterval)), bInterval);
316 if (BoundVariable (ctx, b, yInterval, boundingInterval, y, result, out isBottom, upperBounded, lowerBounded))
322 static IDictionary<TVar, Sequence<TInterval>> TestTrueLessEqualThan_AxByLtK<TEnv, TVar, TExpr, TInterval>
323 (Polynomial<TVar, TExpr> poly, TEnv env, IDictionary<TVar, Sequence<TInterval>> result, out bool isBottom)
324 where TEnv : IIntervalEnvironment<TVar, TExpr, TInterval, Rational>
325 where TVar : IEquatable<TVar>
326 where TInterval : IntervalBase<TInterval, Rational>
329 var ax = poly.Left[0];
330 var by = poly.Left[1];
333 ax.IsSingleVariable (out x);
334 by.IsSingleVariable (out y);
336 var k = poly.Right[0].Coeff;
340 var aInterval = env.Context.For (a);
341 var bInterval = env.Context.For (b);
342 var kInterval = env.Context.For (k);
344 var xInterval = env.Eval (x);
345 var yInterval = env.Eval (y);
347 IntervalContextBase<TInterval, Rational> ctx = env.Context;
348 Func<Rational, TInterval> upperBounded =
349 (i) => ctx.For (Rational.MinusInfinity, !i.IsInteger ? i.PreviousInt32 : i - 1L);
350 Func<Rational, TInterval> lowerBounded =
351 (i) => ctx.For (!i.IsInteger ? i.NextInt32 : i + 1L, Rational.PlusInfinity);
353 // x <= (k - (b * y)) / a;
354 var boundingInterval = ctx.Div (ctx.Sub (kInterval, ctx.Mul (bInterval, yInterval)), aInterval);
355 if (BoundVariable (ctx, a, xInterval, boundingInterval, x, result, out isBottom, upperBounded, lowerBounded))
358 // y <= (k - (a * x)) / b;
359 boundingInterval = ctx.Div (ctx.Sub (kInterval, ctx.Mul (aInterval, xInterval)), bInterval);
360 if (BoundVariable (ctx, b, yInterval, boundingInterval, y, result, out isBottom, upperBounded, lowerBounded))
367 /// Get constraints for variables from polynome in form 'a*x <= k'
369 /// <param name="poly">Polynome in canonical form. Two monomes involved. </param>
370 static IDictionary<TVar, Sequence<TInterval>> TestTrueLessEqualThan_AxLeqK<TEnv, TVar, TExpr, TInterval>
371 (Polynomial<TVar, TExpr> poly, TEnv env, IDictionary<TVar, Sequence<TInterval>> result, out bool isBottom)
372 where TEnv : IIntervalEnvironment<TVar, TExpr, TInterval, Rational>
373 where TVar : IEquatable<TVar>
374 where TInterval : IntervalBase<TInterval, Rational>
378 var ax = poly.Left[0];
379 var k = poly.Right[1];
381 if (ax.Coeff > k.Coeff) {
388 ax.IsSingleVariable (out x);
390 if (Rational.TryDivide (k.Coeff, ax.Coeff, out div)) {
391 var intv = env.Eval (x);
393 var refined = ax.Coeff.Sign < 1
394 ? intv.Meet (env.Context.For (div, Rational.PlusInfinity))
395 : intv.Meet (env.Context.For (Rational.MinusInfinity, div));
397 AddToResult (result, x, refined);
405 /// Get constraints for variables from polynome in form 'a*x <= k'
407 /// <param name="poly">Polynome in canonical form. Two monomes involved. </param>
408 static IDictionary<TVar, Sequence<TInterval>> TestTrueLessEqualThan_AxLtK<TEnv, TVar, TExpr, TInterval>
409 (Polynomial<TVar, TExpr> poly, TEnv env, IDictionary<TVar, Sequence<TInterval>> result, out bool isBottom)
410 where TEnv : IIntervalEnvironment<TVar, TExpr, TInterval, Rational>
411 where TVar : IEquatable<TVar>
412 where TInterval : IntervalBase<TInterval, Rational>
416 var ax = poly.Left[0];
417 var k = poly.Right[1];
419 if (ax.Coeff >= k.Coeff) {
426 ax.IsSingleVariable (out x);
428 if (Rational.TryDivide (k.Coeff, ax.Coeff, out div)) {
429 var intv = env.Eval (x);
431 var boundByDivPlus = !div.IsInteger
432 ? env.Context.For (div.NextInt32, Rational.PlusInfinity)
433 : env.Context.For (div + 1L, Rational.PlusInfinity);
434 var boundByDivMinus = !div.IsInteger
435 ? env.Context.For (Rational.MinusInfinity, div.NextInt32 - 1L)
436 : env.Context.For (Rational.MinusInfinity, div - 1L);
437 var refined = intv.Meet (ax.Coeff.Sign < 1 ? boundByDivPlus : boundByDivMinus);
439 if (refined.IsBottom) {
444 AddToResult (result, x, refined);
450 static bool BoundVariable<TVar, TInterval>
451 (IntervalContextBase<TInterval, Rational> ctx, Rational a, TInterval xIntervalOld, TInterval boundingInterval, TVar x,
452 IDictionary<TVar, Sequence<TInterval>> result, out bool isBottom,
453 Func<Rational, TInterval> upperBounded, Func<Rational, TInterval> lowerBounded)
454 where TVar : IEquatable<TVar>
455 where TInterval : IntervalBase<TInterval, Rational>
459 TInterval boundingForVariable;
461 boundingForVariable = upperBounded (boundingInterval.UpperBound);
463 boundingForVariable = lowerBounded (boundingInterval.LowerBound);
465 var refined = xIntervalOld.Meet (boundingForVariable);
466 if (refined.IsBottom) {
471 AddToResult (result, x, refined);
476 static bool IsFloat<TVar, TExpr> (TExpr expr, IExpressionDecoder<TVar, TExpr> decoder)
481 var type = decoder.TypeOf (expr);
482 return type == ExpressionType.Float32 || type == ExpressionType.Float64;
485 static void AddToResult<TVar, TInterval> (IDictionary<TVar, Sequence<TInterval>> result, TVar variable, TInterval intv)
486 where TVar : IEquatable<TVar>
487 where TInterval : IntervalBase<TInterval, Rational>
489 Sequence<TInterval> value;
490 result.TryGetValue (variable, out value);
492 result[variable] = value.Cons (intv);
496 public struct InferenceResult<TVar, TInterval>
497 where TInterval : IAbstractDomain<TInterval>
498 where TVar : IEquatable<TVar> {
499 public static readonly InferenceResult<TVar, TInterval> Empty =
500 new InferenceResult<TVar, TInterval> (false, ImmutableMap<TVar, Sequence<TInterval>>.Empty);
502 public readonly bool IsBottom;
504 public readonly IImmutableMap<TVar, Sequence<TInterval>> Constraints;
506 InferenceResult (bool isbottom, IImmutableMap<TVar, Sequence<TInterval>> map)
512 public InferenceResult<TVar, TInterval> AddConstraintFor (TVar rightVar, TInterval refined)
514 Sequence<TInterval> seq;
515 Constraints.TryGetValue (rightVar, out seq);
517 return new InferenceResult<TVar, TInterval> (IsBottom, Constraints.Add (rightVar, seq.Cons (refined)));
520 public InferenceResult<TVar, TInterval> Join (InferenceResult<TVar, TInterval> that)
528 foreach (var var in Constraints.Keys) {
529 var leftContraints = Constraints[var];
530 if (leftContraints == null)
533 // tops are not included
534 Sequence<TInterval> rightConstraints;
535 if (that.Constraints.TryGetValue (var, out rightConstraints) && rightConstraints != null) {
536 var intv = leftContraints.Head.Join (rightConstraints.Head);
538 result.AddConstraintFor (var, intv);