Widening and Narrowing in Variable Range Analysis
Interval analysis domain, for each program point, maps each program variable to an interval.
Analysis domain has elements
where
denotes the set of such intervals.
Height of lattice for unbounded integers: infinite.
Height of lattice of one interval for 64-bit integers: around
Moreover, if we have
variables in program and
program points, height of lattice for the analysis domain is
times larger.
How to guarantee (reasonably fast) termination?
Widening technique: if the iteration does not seem to be converging, take a “jump” and make the interval much wider (larger).
Finite set of jump points
(e.g. set of all integer constants in the program)
In fixpoint computation, compose
with function
We require the condition:
for all
.
The condition holds for the example above.
Approaches:
- always apply widening (we will assume this)
- iterate a few times with
only (without using
), if we are not at a fixpoint at this program point, then widen.- this is not monotonic: if you start at fixpoint, it converges, if start below, can jump over fixpoint!
Standard iteration:
Widening:
Example where widening works nicely
Consider program:
x = 0; while (x < 1000) { x = x + 1; }
Interval analysis without widening will need around 1000 iterations to converge to interval
for
at the end of the program. This
may be too slow.
Let us derive the set
by taking all constants
that appear in the program, as well as
and
:
After a few iterations, widening maps interval
into
. This gives
for
at loop entry and again
for
at
the end of the program, but in many fewer iterations.
Example showing problems with widening
Consider program:
x = 0; y = 1; while (x < 1000) { x = x + 1; y = 2*x; y = y + 1; print(y); }
Interval analysis without widening will need around 1000 iterations to converge to
This may be too slow.
Now apply widening with the same
as before.
When within loop we obtain
, applying widening function to the interval
for
results in
.
We obtain
at the end of the program:
Narrowing
Observation
Consider a monotonic function, such as
on the set of real numbers. If we consider a sequence
this sequence is monotonically increasing iff
(e.g. for
) and it is monotonically decreasing iff
(e.g. for
). Informally, the sequence continues of the direction in which it starts in the first step.
This is because
implies by monotonicity of
that
etc., whereas
implies
.
The Idea
Let
such that
.
After finding fixpoint of
, apply
to improve precision.
Widen and Narrow
Lemma: Let
and
be monotonic functions on a partial order
such that
for all
. Define the following:
where we also assume that the two
and one
exist. Then
.
Proof:
By induction, for each
we have
Thus by Comparing Fixpoints of Sequences, we have
.
Next, we have that
Thus,
. From there by induction and monotonicity of
we obtain
i.e. the sequence
is decreasing. Therefore,
is its upper bound and therefore
.
On the other hand, we have by monotonicity of
, the fact
that
is fixpoint, and
that:
Thus,
is the lower bound on
, so
.
End of proof.
Note: even if
does not exist, we can simply compute
for any chosen value of
, it is still a sound over-approximation, because it approximates
, which approximates the concrete value:
so
Being able to stop at any point gives us an anytime algorithm.
Example
Example showing how narrowing may improve result after widening
In the above example for the program, the results obtained using widening are:
x = 0; y = 1; // x -> [0,0], y -> [1,1] // (merge point) // x -> [0,1000], y -> [1,+infty) while (x < 1000) { // x -> [0,999], y -> [1,+infty) x = x + 1; // x -> [0,1000], y -> [1,+infty) y = 2*x; // x -> [0,1000], y -> [0,+infty) y = y + 1; // x -> [0,1000], y -> [1,+infty) print(y); } // x -> [1000,1000], y -> [1,+infty)
Let us now apply one ordinary iteration, without widening. We obtain:
x = 0; y = 1; // x -> [0,0], y -> [1,1] // (merge point) // x -> [0,1000], y -> [1,2001] while (x < 1000) { // x -> [0,999], y -> [1,+infty) x = x + 1; // x -> [0,1000], y -> [1,+infty) y = 2*x; // x -> [0,1000], y -> [0,2000] y = y + 1; // x -> [0,1000], y -> [1,2001] print(y); } // x -> [1000,1000], y -> [1,2001]
Thus, we obtained a good first approximation by a few iterations with widening and then improved it with a single iteration without widening.



(by