Inheritance | Verifiable Software

]]>Inheritance | Verifiable Software

]]>the definition of your function foo is recursive and the execution of this

function in a naive way recursively is highly imperformant. This statement is

true for the verifier and for any runtime execution as well.

Let me make some general comments first and then adress the performance of your

specific function foo.

1. Needed calculations and compile/verfication time

In my experience actual calculations are rarely necessary during

verification. The question whether a function f returns a specific value when

fed with a constant is usually not necessary. Why do you want the verifier to

answer the question “Is 4! = 24 true?”? You usually ask the verifier if your

specific implementation of the factorial function returns the same result as

the mathematical definition. But in some cases actual calculations are

necessary. Since the verifier will do calculations (if needed) in an

interpretative manner, the performance of this needed calculations will be

slower than the calculations done by software compiled to native code.

2. Recursive and iterative evaluation of a function

Your function foo is highly imperformant if executed recursively. Its like the

fibonacci function. When you do recursive evalutation you get bad performance

and when you do iterative evaluation you get excellent results. Therefore my

language allows to give a definition/specification of a function and an

implementation of a function.

In the case of your function foo I would use the recursive definition as the

specification and provide an iterative implementation.

```
foo(i:NATURAL): FLOAT
do
if i<3 then
Result := i/3.33
else
local
a,b,c:FLOAT
j: NATURAL
do
from
j := 2
a,b,c := 0/3.33, 1/3.33, 2/3.33
invariant
2 <= j <= i
a = foo(j-2)
b = foo(j-1)
c = foo(j)
variant
i - j
until
i = j
loop
j,a,b,c :=
j+1,
b,
c,
a*0.33+b*0.51+c*0.71
end
Result := c
end
end
ensure
Result = if i<3
then i/3.33
else foo(i-1)*0.33
+foo(i-2)*0.51
+foo(i-3)*0.71
end
end
```

For all values of i with i>=3 a loop is used to calculate foo(i). The

important thing for the verifier is the invariant of the loop. At all

iterations the invariant is maintained i.e. it is valid before and after the

body of the loop. Note that the function foo can be used within the assertions

of the implementation because it has a definition/specification (as shown in

the postcondition) which is independent of the implementation of the function.

With this code the execution of the funciton foo will be excellent. This

applies to interpretation and compilation to native code as well. The

calculation of foo(50) requires just 48 iterations with a trivial loop body.

For many functions (including your function foo) the specification and implementation is practically identical. Look at the function times_two

```
times_two(i:NATURAL): NATURAL
ensure Result = 2*i end
```

Each time you can express that result of a function as a closed expression (i.e. in the form Result = …) you have a valid specification. As long as the right hand side of ‘Result=…’ is computable (i.e. contains no ghost functions) the specification is sufficient in my programming language. The compiler derives the implementation automatically. The compiler treats the above definition of ‘times_two’ as if it had been written

```
times_two(i:NATURAL): NATURAL
do
Result := 2*i
ensure
Result = 2*i
end
```

But it is not necessary to write implementation and specification if the specification has the form ‘Result=…’.

Let’s look at your function foo. In my programming language it is sufficient to write

```
foo(i:NATURAL): FLOAT
ensure
Result = if i<3
then i/3.33
else foo(i-1)*0.33
+foo(i-2)*0.51
+foo(i-3)*0.71
end
end
```

As you can see there is practically no difference between my programming language and languages like C#, java, etc. in the complexity of writing such a function.

2. Runtime exceptions and static verification:

Let’s assume you use your function foo to do some indexing operations on an array a like ‘v := a[foo(i).to_natural]’. In java and C# each array access at runtime includes an out of bound check. In case that the expression ‘foo(i).to_natural’ is out of the array bounds you get a runtime exception. What are you going to do if you get such a runtime exception? You certainly will analyze what values can become attached to the variable i and you certainly have to analyze the possible return values of the function ‘foo’.

Static verification requires you to do this analysis upfront. You have to convince the verifier that the expression ‘foo(i).to_natural’ never yields an index out of the bounds of the array ‘a’.

Therefore you gain nothing by not doing any static analysis unless you are willing to accept that your program might have errors which have not yet popped up with the tests you have done up to now.

3. Skills:

It is undeniable that programming with static analysis requires some skills which many programmers do not yet have. Static analysis can be done successfully only if one is capable of writing assertions and capable of reasoning with assertions. But these skills are nothing magic. They can be learned like any other technique (e.g. using a debugger etc.).

]]>All functions need a specification. The type NATURAL->NATURAL is not a specification it is just a type. A specification of times_two can look like

times_two(i:NATURAL): NATURAL ensure Result = 2*i end

Don’t confuse the specification with an implementation (in this trivial case the specification is nearly identical with the implementation).

The compiler/verifier “knows” the specification and can therefore derive ‘2=times_two(1)’.

2. Undecidable problems:

Clearly the verifier cannot resolve problems which are in its general case undecidable. If you state any assertion and expect the verifier to tell you that the assertion is true or false I have to disappoint you. This is impossible!

3. Your function foo:

You designed some function foo. Then you want an answer to the question if there is some i in (1..100) such that foo(i).to_natural = 844 is valid. In order to answer this question I would have to analyze your function definition in detail. Since you have designed this function, you are better suited to analyze it. I can offer you the following: If you can convince yourself that ‘some(i) i in (1..100) and foo(i).to_natural=844’ is valid, write down your arguments and post them here. Then I can show you how to convince that verifier about this fact. If you are not able to convince yourself about this fact don’t expect that you can convince the verifier.

]]>I just gave you the implementation of foo, you’re free to derive any assertions you can from it. Can the verifier check if the arrary read is valid or not in reasonable time?

]]>The loop has the postcondition ‘all(i) i in (1..10) => times_two(i) in a.domain and a[times_two(i)]=i’. With this we can do the reasoning ‘1 in (1..10)’ which implies ‘time_two(1) in a.domain’ which together with ‘2=times_two(1)’ implies ‘2 in a.domain’.

Second example:

The loop has the postcondition ‘all(i) i in (1..100) => foo(i) in a.domain and a[foo(i).to_natural]=i’. In order to proof that ‘844 in a.domain’ we need to proof ‘some(i) i in (1..10) and foo(i).to_natural = 844’. In order to proof this it is necesary to derive some assertions about the function foo.

timesTwo : NATURAL -> NATURAL

for (i in 1..10) {

a[timesTwo(i)] := i

}

b = a[2] // Illegal?

Here’s an example of a costly indexing operation:

foo(i : Int) : Double = if (i < 3) i / 3.33 else foo(i – 1) * 0.33 + foo(i – 2) * 0.51 + foo(i – 3) * 0.71

for (i in 1..100) {

a[foo(i).toInt] := i

}

b = a[844] // Illegal?

]]>