Loops

Preconditions

Files:     [Slides]     [Code examples]

As a preparation for next class, please go over the background material on domain theory such that we can have an informed discussion about loops and procedures in class. You can use this form for questions and unclarities about the reading material. I will try to address your questions in class.

Postconditions

Files:     [Slides with solutions]     [Code examples with solutions]

What you should have learned after completing chapter 4.1:

  • If we allow for arbitrary predicates, then the precise weakest precondition of a loop is given by a fixed point, which states that the weakest precondition equals the weakest precondition of unfolding the loop.
  • When reasoning about total correctness, the weakest precondition corresponds to the least fixed point of said loop unfolding.
  • When reasoning about partial correctness, that is if we do not care about termination, then the weakest liberal precondition corresponds to the greatest fixed point of said loop unfolding.
  • While we can, in principle, represent the above fixed points by predicates in the syntax allowed by PL0, it is typically far too complicated for automated verification tools. We thus use proof rules for reasoning about loops without determining the exact weakest precondition.
  • We typically split the task of verifying loops into two: we use invariants for showing partial correctness and variants to show termination. The correctness of these constructs is justified by reasoning about the fixed points discussed above.
  • A loop invariant is a predicate that must hold before every loop iteration. You should be able to find sufficiently strong loops invariants to verify that a program with a loop satisfies its contract.
  • A variant is an expression over some well-founded ordering that decreases in every loop iteration. If a variant exists, then the loop terminates. You should be able to find simple variants (e.g. using the ordering < over natural numbers) for a given loop.
  • You should be able to automate reasoning with loop invariants and variants by encoding these concepts into PL0.

Homework

Tasks

Submission deadline: Thursday, October 6, 12:59 (right before the lecture).

Please submit your solutions by pushing all relevant files to the GitLab repository assigned to your group. If you have not been assigned a repository yet, contact the teacher.

Task 1 (5 points): Slow square roots

The following Viper program attempts to compute the integer square root of some natural number n. Find a suitable invariant for the line marked with TODO such that the program verifies.

Hint: Notice that the specification admits programs that do not enforce the computation of the integer square root of n. We will address this in class. You should still find a suitable invariant.

method int_sqrt() {
    var n: Int
    assume n >= 0

    var res: Int

    res := 0
    while ((res + 1) * (res + 1) < n)
        invariant false // TODO
    {
        res := res + 1
    }

    assert res * res <= n
        && n <= (res + 1) * (res + 1) 
}

Task 2 (5 points): Fast square roots

The following Viper program attempts to compute the integer square root of some natural number n more efficiently. Find a suitable invariant for the line marked with TODO such that the program verifies.

Hint: Notice that the specification admits programs that do not enforce the computation of the integer square root of n. We will address this in class. You should still find a suitable invariant.

method int_sqrt_fast() {
    var n: Int 
    assume n >= 0

    var res: Int

    res := 0
    var x: Int := 1
    while (x < n)
        invariant false // TODO
    {
        x := x + 2 * res + 3
        res := res + 1
    }

    assert res * res <= n
        && n <= (res + 1) * (res + 1) 
}

Task 3 (10 points): Square without multiplication

Implement and verify the method below such that it returns the square of any given non-negative integer n.

Your implementation must not use recursion or any arithmetic other than constants and +1. That is, x := 0 and x := x + 1 are allowed. However, x := y + z, x := x * y, and x := 2 * x are not allowed.

You may still use arbitrary arithmetic in assertions and invariants.

method square(n: Int) returns (res: Int)
    requires n >= 0
    ensures res == n * n
{
    // TODO
}