In computer science, a loop invariant is a property of a program loop that is true before (and after) each iteration. It is a logical assertion, sometimes checked with a code assertion. Knowing its invariant(s) is essential in understanding the effect of a loop.
In formal program verification, particularly the Floyd-Hoare approach, loop invariants are expressed by formal predicate logic and used to prove properties of loops and by extension algorithms that employ loops (usually correctness properties).The loop invariants will be true on entry into a loop and following each iteration, so that on exit from the loop both the loop invariants and the loop termination condition can be guaranteed.
From a programming methodology viewpoint, the loop invariant can be viewed as a more abstract specification of the loop, which characterizes the deeper purpose of the loop beyond the details of this implementation. A survey article [1] covers fundamental algorithms from many areas of computer science (searching, sorting, optimization, arithmetic etc.), characterizing each of them from the viewpoint of its invariant.
Because of the similarity of loops and recursive programs, proving partial correctness of loops with invariants is very similar to proving the correctness of recursive programs via induction. In fact, the loop invariant is often the same as the inductive hypothesis to be proved for a recursive program equivalent to a given loop.
The following C subroutine max
returns the maximum value in its argument array a[]
, provided its length n
is at least 1.Comments are provided at lines 3, 6, 9, 11, and 13. Each comment makes an assertion about the values of one or more variables at that stage of the function.The highlighted assertions within the loop body, at the beginning and end of the loop (lines 6 and 11), are exactly the same. They thus describe an invariant property of the loop.When line 13 is reached, this invariant still holds, and it is known that the loop condition i!=n
from line 5 has become false. Both properties together imply that m
equals the maximum value in a[0...n-1]
, that is, that the correct value is returned from line 14.i!=n
in line 5 should better be modified to i<n
, in order to avoid endless looping for illegitimate negative values of n
. While this change in code intuitively shouldn't make a difference, the reasoning leading to its correctness becomes somewhat more complicated, since then only i>=n
is known in line 13. In order to obtain that also i<=n
holds, that condition has to be included into the loop invariant. It is easy to see that i<=n
, too, is an invariant of the loop, since i<n
in line 6 can be obtained from the (modified) loop condition in line 5, and hence i<=n
holds in line 11 after i
has been incremented in line 10. However, when loop invariants have to be manually provided for formal program verification, such intuitively too obvious properties like i<=n
are often overlooked.
In Floyd–Hoare logic,[2] [3] the partial correctness of a while loop is governed by the following rule of inference:
\{C\landI\ | |
body \{I\}} |
{\{I\} while (C) body \{lnotC\landI\}}
This means:
body
body
while (C) body
\{C\landI\} body \{I\}
C\landI
body
while (C) body
lnotC\landI
With some variations in the notation used, and with the premise that the loop halts, this rule is also known as the Invariant Relation Theorem.[4] [5] As one 1970s textbook presents it in a way meant to be accessible to student programmers:[4]
Let the notation P { seq } Q
mean that if P
is true before the sequence of statements seq
run, then Q
is true after it. Then the invariant relation theorem holds that
P & c { seq } P
implies
P { DO WHILE (c); seq END; } P & ¬c
The following example illustrates how this rule works. Consider the program
while (x < 10) x := x+1;
One can then prove the following Hoare triple:
\{x\leq10\} while (x<10) x:=x+1 \{x=10\}
The condition C of the while
loop is
x<10
x\leq10
\{x<10\landx\leq10\} x:=x+1 \{x\leq10\}
While this triple can be derived formally from the rules of Floyd-Hoare logic governing assignment, it is also intuitively justified: Computation starts in a state where
x<10\landx\leq10
x<10
x\leq10
Under this premise, the rule for while
loops permits the following conclusion:
\{x\leq10\} while (x<10) x:=x+1 \{lnot(x<10)\landx\leq10\}
However, the post-condition
lnot(x<10)\landx\leq10
x=10
The property
0\leqx
true
\{0\leqx\} while (x<10) x:=x+1 \{10\leqx\}
true
\{true\} while (x<10) x:=x+1 \{10\leqx\}
The Eiffel programming language provides native support for loop invariants.[6] A loop invariant is expressed with the same syntax used for a class invariant. In the sample below, the loop invariant expression x <= 10
must be true following the loop initialization, and after each execution of the loop body; this is checked at runtime.
The Whiley programming language also provides first-class support for loop invariants.[7] Loop invariants are expressed using one or more where
clauses, as the following illustrates:
The max
function determines the largest element in an integer array. For this to be defined, the array must contain at least one element. The postconditions of max
require that the returned value is: (1) not smaller than any element; and, (2) that it matches at least one element. The loop invariant is defined inductively through two where
clauses, each of which corresponds to a clause in the postcondition. The fundamental difference is that each clause of the loop invariant identifies the result as being correct up to the current element i
, whilst the postconditions identify the result as being correct for all elements.
A loop invariant can serve one of the following purposes:
For 1., a natural language comment (like // m equals the maximum value in a[0...i-1]
in the above example) is sufficient.
For 2., programming language support is required, such as the C library assert.h, or the above-shown invariant
clause in Eiffel. Often, run-time checking can be switched on (for debugging runs) and off (for production runs) by a compiler or a runtime option.
For 3., some tools exist to support mathematical proofs, usually based on the above-shown Floyd–Hoare rule, that a given loop code in fact satisfies a given (set of) loop invariant(s).
The technique of abstract interpretation can be used to detect loop invariant of given code automatically. However, this approach is limited to very simple invariants (such as 0<=i && i<=n && i%2==0
).
See also: Loop-invariant code motion. Loop-invariant code consists of statements or expressions that can be moved outside a loop body without affecting the program semantics. Such transformations, called loop-invariant code motion, are performed by some compilers to optimize programs.A loop-invariant code example (in the C programming language) isx = y+z
and x*x
can be moved before the loop, resulting in an equivalent, but faster, program:0<=i && i<=n
is a loop invariant for both the original and the optimized program, but is not part of the code, hence it doesn't make sense to speak of "moving it out of the loop".
Loop-invariant code may induce a corresponding loop-invariant property. For the above example, the easiest way to see it is to consider a program where the loop invariant code is computed both before and within the loop:(x1==x2 && t1==x2*x2) || i==0
, indicating that the values computed before the loop agree with those computed within (except before the first iteration).