I'm trying to define an operation on classes and then prove properties about it.
//the following resolves the error with substring, but creates problems down the line
//predicate isSubstring<A(!new)>(sub: seq<A>, super: seq<A>) {
predicate isSubstring<A>(sub: seq<A>, super: seq<A>) {
|sub| <= |super| && exists xs: seq<A> :: IsSuffix(xs, super) && sub <= xs
}
predicate IsSuffix<T>(xs: seq<T>, ys: seq<T>) {
|xs| <= |ys| && xs == ys[|ys| - |xs|..]
}
However, the IsSubstring method produces the following error.
a exists expression involved in a predicate definition is not allowed to depend on the set of allocated references, but values of 'xs' may contain references (see documentation for 'older' parameters
I am aware that I can set (!new) restriction on the type variable A. That resolves the issue at the predicate. However I am presented with another issue.
lemma AllChildrenTraversalsAreSubstrings(root: TreeNode)
requires root.Valid()
ensures forall x :: x in root.repr && x in PreorderTraversal(root) ==> isSubstring(PreorderTraversal(x), PreorderTraversal(root))
{
forall x | x in root.repr && x in PreorderTraversal(root)
ensures isSubstring(PreorderTraversal(x), PreorderTraversal(root))
{
if x == root {
}else if x == root.left || x == root.right {
PreorderTraversalSubstrings(root);
}else {
if root.left != null && x in root.left.repr {
AllChildrenTraversalsAreSubstrings(root.left);
}
if root.right != null && x in root.right.repr {
AllChildrenTraversalsAreSubstrings(root.right);
}
}
}
}
In the forall ensure it reports:
type parameter (A) passed to predicate isSubstring must support no references (got TreeNode)
function method PreorderTraversal(root: TreeNode): seq<TreeNode>
reads root.repr
requires root.Valid()
// ensures forall x :: x in PreorderTraversal(root) ==> x.Valid()
ensures forall x :: x in root.repr ==> x in PreorderTraversal(root)
ensures forall k :: 0 <= k < |PreorderTraversal(root)| ==> PreorderTraversal(root)[k] in root.repr && PreorderTraversal(root)[k].Valid()
ensures injectiveSeq(PreorderTraversal(root))
ensures forall k :: 0 <= k < |PreorderTraversal(root)| ==> PreorderTraversal(root)[k] in root.repr
// ensures forall k :: 0 <= k < |PreorderTraversal(root)| ==> forall child :: child in PreorderTraversal(root)[k].repr && child != child in PreorderTraversal(root)[k] ==> exists j :: k < j < |PreorderTraversal(root)| && PreorderTraversal(root)[j] == child
{
if root.left != null && root.right != null then [root]+PreorderTraversal(root.left)+PreorderTraversal(root.right) else if root.left != null then [root]+PreorderTraversal(root.left) else if root.right != null then [root]+PreorderTraversal(root.right) else [root]
}
lemma PreorderTraversalSubstrings(root: TreeNode)
requires root.Valid()
ensures root.left != null ==> isSubstring(PreorderTraversal(root.left), PreorderTraversal(root))
ensures root.right != null ==> isSubstring(PreorderTraversal(root.right), PreorderTraversal(root))
{
if root.left != null && root.right != null {
calc {
PreorderTraversal(root);
[root]+PreorderTraversal(root.left)+PreorderTraversal(root.right);
}
assert |PreorderTraversal(root.left)| < |PreorderTraversal(root)|;
assert |PreorderTraversal(root.right)| < |PreorderTraversal(root)|;
assert IsSuffix(PreorderTraversal(root.left)+PreorderTraversal(root.right), PreorderTraversal(root));
assert IsSuffix(PreorderTraversal(root.right), PreorderTraversal(root));
assert PreorderTraversal(root.left) <= PreorderTraversal(root.left)+PreorderTraversal(root.right);
}else if root.left != null && root.right == null {
calc {
PreorderTraversal(root);
[root]+PreorderTraversal(root.left);
}
assert |PreorderTraversal(root.left)| < |PreorderTraversal(root)|;
assert IsSuffix(PreorderTraversal(root.left), PreorderTraversal(root));
}else if root.left == null && root.right != null {
calc {
PreorderTraversal(root);
[root]+PreorderTraversal(root.right);
}
assert |PreorderTraversal(root.right)| < |PreorderTraversal(root)|;
assert IsSuffix(PreorderTraversal(root.right), PreorderTraversal(root));
}
}
class TreeNode {
var val: int;
var left: TreeNode?;
var right: TreeNode?;
ghost var repr: set<TreeNode>;
constructor(val: int, left: TreeNode?, right: TreeNode?)
requires left != null ==> left.Valid()
requires right != null ==> right.Valid()
requires left != null && right != null ==> left.repr !! right.repr
ensures this.val == val
ensures this.left == left
ensures this.right == right
ensures left != null ==> this !in left.repr
ensures right != null ==> this !in right.repr
ensures Valid()
{
this.val := val;
this.left := left;
this.right := right;
var leftRepr := if left != null then {left}+left.repr else {};
var rightRepr := if right != null then {right}+right.repr else {};
this.repr := {this} + leftRepr + rightRepr;
}
predicate Valid()
reads this, repr
decreases repr
{
this in repr &&
(this.left != null ==>
(this.left in repr
&& this !in this.left.repr
&& this.left.repr < repr
&& this.left.Valid()
))
&& (this.right != null ==>
(this.right in repr
&& this !in this.right.repr
&& this.right.repr < repr
&& this.right.Valid())) &&
(this.left != null && this.right != null ==> this.left.repr !! this.right.repr && this.repr == {this} + this.left.repr + this.right.repr)
&& (this.left != null && this.right == null ==> this.repr == {this} + this.left.repr)
&& (this.right != null && this.left == null ==> this.repr == {this} + this.right.repr)
&& (this.right == null && this.left == null ==> this.repr == {this})
}
}
Oddly enough,PreorderTraversalSubstrings verifies just fine even with the (!new) restriction but the ensure statement of the forall in AllChildrenTraversalsAreSubstrings throws the above error. How should I proceed? Switching to a datatype would make my life easier but I'm trying to verify programs involving classes.
Do I define the binary tree datatype and then assert that all operations on it are equivalent to the valid class tree version? Is that even possible if existence quantifier expressions can't refer to allocated value?
Trying to setup a few functions for a quicksort implementation I got stuck on the following lemmas, filterLemmaExtra and filterLemmaSizes.
function filter<T(==)>(xs: seq<T>, p: (T) -> bool): seq<T>
ensures forall x: T :: x in xs && p(x) ==> x in filter(xs, p)
ensures forall x: T :: x !in xs && p(x) ==> x !in filter(xs, p)
ensures forall x: T :: x in filter(xs, p) ==> p(x)
ensures forall x: T :: x in filter(xs, p) ==> x in xs[0..|xs|]
ensures forall x: T :: x in filter(xs, p) ==> x in xs
ensures forall x: T :: x in xs && !p(x) ==> x !in filter(xs, p)
ensures forall i: nat :: i < |filter(xs, p)| ==> filter(xs, p)[i] in xs
{
if xs == [] then [] else if p(xs[0]) then [xs[0]] + filter(xs[1..], p) else filter(xs[1..], p)
}
lemma filterLemmaSizes<T(==)>(xs: seq<T>, fxs: seq<T>, p: (T) -> bool)
requires fxs == filter(xs, p)
ensures forall x: T :: x in xs && p(x) ==> multiset(xs)[x] == multiset(fxs)[x]
ensures multiset(filter(xs,p)) <= multiset(xs)
{
}
lemma filterLemmaExtra<T(==)>(xs: seq<T>, p: (T) -> bool, i: nat)
requires 0 <= i <= |xs|
ensures filter(xs, p) == filter(xs[0..i], p) + filter(xs[i..], p)
{
}
predicate isNegatedBooleanFn<T(==)>(xs: seq<T>, p: (T) -> bool, q: (T) -> bool) {
forall x: T :: x in xs && p(x) ==> !q(x)
}
function filter_mset<T(==)>(ms: multiset<T>, p: (T) -> bool): multiset<T>
ensures forall x :: x in ms && p(x) ==> x in filter_mset(ms, p) && ms[x] == filter_mset(ms, p)[x]
ensures forall x :: x in filter_mset(ms, p) ==> p(x)
ensures forall x :: x in filter_mset(ms, p) ==> x in ms
{
if ms == multiset{} then multiset{} else
var x :| x in ms; if p(x) then var result := multiset{}; result[x := ms[x]] + filter_mset(ms[x := 0], p) else filter_mset(ms[x := 0], p)
}
lemma filterAndFilterMset<T(==)>(ms: seq<T>, p: (T) -> bool)
ensures multiset(filter(ms, p)) == filter_mset(multiset(ms), p)
{
assert forall x :: x in filter(ms, p) ==> x in multiset(filter(ms, p)) && p(x);
assert forall x :: x in filter(ms, p) ==> x in filter_mset(multiset(ms), p);
assert forall x :: x in filter_mset(multiset(ms), p) ==> x in filter(ms, p);
filterLemmaSizes(ms, filter(ms, p), p);
assert forall x :: x in filter(ms, p) ==> multiset(filter(ms, p))[x] == filter_mset(multiset(ms), p)[x];
}
lemma filterMS<T(==)>(xs: seq<T>, p: (T) -> bool)
ensures exists q: (T) -> bool :: isNegatedBooleanFn(xs, p, q)
{
var q: (T) -> bool := y => !p(y);
forall x | x in xs
ensures x in xs && p(x) ==> !q(x)
{
if p(x) {
assert !q(x);
}
}
assert isNegatedBooleanFn(xs, p, q);
// assert forall x: T :: x in xs && p(x) ==> !q(x);
}
lemma filterMsetAndSum<T(==)>(xs: seq<T>, ms: multiset<T>, p: (T) -> bool)
requires ms == multiset(xs)
ensures exists Q: (T) -> bool :: isNegatedBooleanFn(xs, p, Q) && (filter_mset(ms, p) + filter_mset(ms, Q)) == ms
{
filterMS(xs, p);
var Q :| isNegatedBooleanFn(xs, p, Q);
var sum_ms := filter_mset(ms, p) + filter_mset(ms, Q);
forall x | x in ms
ensures ms[x] == sum_ms[x]
{
if p(x) {
assert x in filter_mset(ms, p);
assert filter_mset(ms, p)[x] == ms[x];
assert x in sum_ms;
assert sum_ms[x] == ms[x];
}else {
assert x in filter_mset(ms, Q);
assert filter_mset(ms, Q)[x] == ms[x];
assert x in sum_ms;
assert sum_ms[x] == ms[x];
}
}
assert sum_ms == ms;
}
My initial implementation of filterLemmaExtra gets bogged down when I try to assert the indices of the concatenated sequences are equal to the filter.
lemma filterLemmaExtra<T(==)>(xs: seq<T>, p: (T) -> bool, i: nat)
requires 0 <= i <= |xs|
ensures filter(xs, p) == filter(xs[0..i], p) + filter(xs[i..], p)
{
assert xs == xs[0..i] + xs[i..];
var allxs := set x | x in xs && p(x);
var leftxs := set x | x in xs[0..i] && p(x);
var rightxs := set x | x in xs[i..] && p(x);
assert allxs == leftxs + rightxs;
forall x | x in filter(xs, p)
ensures x in filter(xs[0..i], p) || x in filter(xs[i..], p)
{
assert x in xs ==> x in xs[0..i] || x in xs[i..];
}
var all := filter(xs[0..i], p) + filter(xs[i..], p);
assert |filter(xs, p)| == |all|;
// forall i: nat | i < |filter(xs,p)| //explodes
// ensures filter(xs, p)[i] == (filter(xs[0..i], p) + filter(xs[i..], p))[i]
// {
// }
}
For the filterLemmaSizes I thought of two approaches. Initially trying to break down the seqences and the filtered sequence but apart from the case that the first element in both sequences match I can't see how to do induction on the rest of the cases.
Then I thought maybe that I could try to do a proof by negation on the multiset values but I'm not sure of how to write those statements. It seems you should be able to assert that that if multiset(xs)[x] == #non-zero number then there exist that many indices in the original array that satisfy p(x) and so they should also be in filter(xs, p);.
lemma filterLemmaSizes<T(==)>(xs: seq<T>, fxs: seq<T>, p: (T) -> bool)
requires fxs == filter(xs, p)
ensures forall x: T :: x in xs && p(x) ==> multiset(xs)[x] == multiset(fxs)[x]
ensures multiset(filter(xs,p)) <= multiset(xs)
{
forall x | x in xs && p(x)
ensures multiset(xs)[x] == multiset(fxs)[x]
{
assert x in multiset(xs);
assert x in xs[0..|xs|];
assert x in multiset(fxs);
assert x in fxs[0..|fxs|];
if multiset(xs)[x] != multiset(fxs)[x] && multiset(xs)[x] < multiset(filter(xs, p))[x] {
} else if multiset(xs)[x] != multiset(fxs)[x] && multiset(xs)[x] > multiset(filter(xs, p))[x] {
}
// if xs != [] && p(xs[0]) && x == xs[0] {
// assert xs == [xs[0]] + xs[1..];
// assert multiset(xs) == multiset{xs[0]} + multiset(xs[1..]);
// assert multiset(xs)[x] == multiset{xs[0]}[x] + multiset(xs[1..])[x];
// assert multiset(xs)[x] == multiset{xs[0]}[x] + multiset(xs[1..])[x];
// assert xs[0] == fxs[0];
// assert multiset(fxs) == multiset{xs[0]} + multiset(filter(xs[1..],p));
// assert x in xs;
// if x in xs[1..] {
// calc {
// multiset(xs)[x];
// ==
// multiset{x}[x] + multiset(xs[1..])[x];
// == {assert 1 == multiset{xs[0]}[x];}
// 1 + multiset(xs[1..])[x];
// == { filterLemmaSizes(xs[1..], filter(xs[1..],p), p); }
// 1 + multiset(filter(xs[1..], p))[x];
// ==
// multiset{xs[0]}[x] + multiset(filter(xs[1..],p))[x];
// ==
// multiset(fxs)[x];
// }
// } else{
// assert multiset(xs[1..])[x] == 0;
// assert multiset(filter(xs[1..], p))[x] == 0;
// }
// assert multiset(xs)[xs[0]] == multiset(fxs)[xs[0]];
// } else if xs != [] && x != xs[0] {
// assert xs[0] == fxs[0];
// } else{
// }
}
}
Proving both lemma need appeal to induction. See the code snippet below, I have n't proved first post condition in second lemma but it should be doable using induction too.
function filter<T>(s: seq<T>, p: T -> bool) : seq<T>
{
if s == [] then []
else if p(s[0]) then [s[0]] + filter(s[1..], p)
else filter(s[1..], p)
}
lemma filterSplit<T>(s: seq<T>, p: T -> bool, idx: nat)
requires 0 <= idx <= |s|
ensures filter(s, p) == filter(s[..idx], p) + filter(s[idx..], p)
{
if idx == 0 {
}
else {
filterSplit(s[1..], p, idx-1);
assert filter(s[1..], p) == filter(s[1..][..(idx-1)], p) + filter(s[1..][(idx-1)..], p);
if p(s[0]) {
calc {
filter(s, p);
[s[0]] + filter(s[1..], p);
[s[0]] + filter(s[1..][..(idx-1)], p) + filter(s[1..][(idx-1)..], p);
{
assert s[..idx] == [s[0]] + s[1..idx];
assert s[1..idx] == s[1..][..(idx-1)];
}
filter(s[..idx], p) + filter(s[1..][(idx-1)..], p);
{
assert s[1..][(idx-1)..] == s[idx..];
}
filter(s[..idx], p) + filter(s[idx..], p);
}
}
else {}
}
}
lemma filterMultiSet<T>(s: seq<T>, p: T -> bool)
ensures multiset(filter(s, p)) <= multiset(s)
{
if s == [] {
}
else {
filterMultiSet(s[1..], p);
calc <= {
multiset(filter(s, p));
multiset([s[0]]) + multiset(filter(s[1..], p));
multiset([s[0]]) + multiset(s[1..]);
{
assert s == [s[0]] + s[1..];
}
multiset(s);
}
}
}
Update : See code snippet below for first postcondition of second lemma
function filter<T>(s: seq<T>, p: T -> bool) : seq<T>
ensures forall x :: x !in s ==> x !in filter(s, p)
{
if s == [] then []
else if p(s[0]) then [s[0]] + filter(s[1..], p)
else filter(s[1..], p)
}
lemma filterIncludeMultiSet<T>(s: seq<T>, p: T -> bool)
ensures forall x :: x in s && p(x) ==> multiset(s)[x] == multiset(filter(s, p))[x]
{
if s == [] {}
else {
var rs := s[1..];
filterIncludeMultiSet(rs, p);
assert forall x :: x in rs && p(x) ==> multiset(rs)[x] == multiset(filter(rs, p))[x];
forall x | x in s && p(x) ensures multiset(s)[x] == multiset(filter(s, p))[x] {
if x == s[0] {
if x in rs {
calc {
multiset(s)[x];
{
assert s == [s[0]] + rs;
assert multiset(s) == multiset([s[0]]) + multiset(rs);
}
multiset([s[0]])[x] + multiset(rs)[x];
1 + multiset(filter(rs, p))[x];
}
}
else {
calc {
multiset(s)[x];
{
assert s == [s[0]] + rs;
assert multiset(s) == multiset([s[0]]) + multiset(rs);
}
multiset([s[0]])[x] + multiset(rs)[x];
1;
}
calc {
multiset(filter(s, p))[x];
multiset([s[0]] + filter(rs, p))[x];
multiset([s[0]])[x] + multiset(filter(rs, p))[x];
1;
}
}
}
else {
calc {
multiset(s)[x];
{
assert s == [s[0]] + rs;
assert multiset(s) == multiset([s[0]]) + multiset(rs);
}
multiset([s[0]])[x] + multiset(rs)[x];
multiset(rs)[x];
multiset(filter(rs, p))[x];
}
}
}
}
I found another way to verify the filterLemmaSizes.
lemma filterLemmaSizes<T(!new)>(xs: seq<T>, fxs: seq<T>, p: (T) -> bool)
requires fxs == filter(xs, p)
ensures forall x: T :: x in xs && p(x) ==> multiset(xs)[x] == multiset(fxs)[x]
{
if xs == [] {
} else {
assert xs == [xs[0]] + xs[1..];
filterLemmaSizes(xs[1..], filter(xs[1..], p), p);
if p(xs[0]) {
calc {
multiset(fxs)[xs[0]];
==
multiset(filter(xs[..1], p))[xs[0]] + multiset(filter(xs[1..], p))[xs[0]];
==
multiset(filter([xs[0]], p))[xs[0]] + multiset(filter(xs[1..], p))[xs[0]];
==
multiset([xs[0]])[xs[0]] + multiset(filter(xs[1..], p))[xs[0]];
==
1 + multiset(filter(xs[1..], p))[xs[0]];
==
multiset{xs[0]}[xs[0]] + multiset(xs[1..])[xs[0]];
==
multiset(xs)[xs[0]];
}
} else{
assert xs[0] !in filter(xs, p);
}
}
}
I'm trying to prove a little lemma for a larger proof, the lemma definition is below:
lemma LoopLemma(a: seq<int>, b: seq<int>, c: seq<int>, k:int, i:int, j:int)
requires 0 <= i < |a| && 0<= j < |b| && 0 <= k < |c| && i +j ==k && |a| + |b| == |c|
requires Sorted(c[..k]) && Sorted(b) && Sorted(a)
requires multiset(c[..k]) == multiset(a[..i]+b[..j])
ensures Sorted(c[..k]+[b[j]]) && Sorted(c[..k]+[a[i]])
{
assert multiset(c[..k]) == multiset(a[..i]+b[..j]);
var q:=a[..i]+b[..j];
var c1 := c[..k];
assert Sorted(c1);
assert multiset(c1) == multiset(q);
assert |q| == i + j;
assert |c1| == k == i + j;
calc {
multiset(c1) == multiset(q);
==
forall l :: l in multiset(c1) ==> l in multiset(q);
== {assert forall l :: l in multiset(q) ==> exists r :: 0 <= r <|q| && l == q[r]; assert forall l :: l in multiset(c1) ==> exists r :: 0 <= r <|c1| && l == c1[r];}
forall l :: 0<=l <|c1| ==> exists r :: 0 <= r < |q| && q[r] == c1[l];
}
}
I get "the calculation step between the previous line and this line might not hold" for the last step, and I don't understand why.
All I'm saying there is that if the multisets of two sequences are equal, for any entry in the first sequence exists an entry in the second sequence with the same value.
I tried some simpler examples (where I define the sequences explicitly) and it worked. Maybe I don't understand something about multisets?
Any suggestions will help.
Additional hint that needed here is every element in sequence is in multiset i.e forall i :: 0 <= i < |c[..k]| ==> c[..k][i] in multiset(c[..k]) (hint in forward direction of reasoning). Following snippet verifies.
predicate Sorted(a: seq<int>)
{
if |a| <= 1 then true else (a[0] <= a[1]) && Sorted(a[1..])
}
lemma LoopLemma(a: seq<int>, b: seq<int>, c: seq<int>, k: int, i: int, j: int)
requires 0 <= i < |a| && 0 <= j < |b| && 0 <= k < |c| && i + j == k && |a| + |b| == |c|
requires Sorted(c[..k]) && Sorted(a) && Sorted(b)
requires multiset(c[..k]) == multiset(a[..i] + b[..j])
{
var s := a[..i] + b[..j];
calc {
multiset(c[..k]) == multiset(s);
forall e :: e in multiset(c[..k]) ==> e in multiset(s);
{
assert forall e :: e in multiset(c[..k]) ==>
exists r :: 0 <= r < |s| && s[r] == e;
// assert forall e :: e in multiset(c[..k]) ==>
// exists r :: 0 <= r < |c[..k]| && c[..k][r] == e;
assert forall i :: 0 <= i < |c[..k]| ==> c[..k][i] in multiset(c[..k]);
}
forall i :: 0 <= i < |c[..k]| ==> exists r :: 0 <= r < |s| && s[r] == c[..k][i];
}
}
I'm following the tutorials here and the code seems correct but when I test using assertions I get an error!
Running the program prints the correct answer, but the assertions seem to be paradoxical. When a show the counter examples it seems that -1 is considered even though it shouldn't be.
method binarySearch(a:array<int>, key:int) returns (r:int)
requires forall i,j :: 0 <= i <= j < a.Length ==> a[i] <= a[j]
ensures 0 <= r ==> r < a.Length && a[r] == key
ensures r < 0 ==> forall i :: 0 <= i < a.Length ==> a[i] != key
{
var lo, hi := 0, a.Length;
while (lo < hi)
invariant 0 <= lo <= hi <= a.Length
invariant forall i :: 0 <= i < lo ==> a[i] < key
invariant forall i :: hi <= i < a.Length ==> a[i] > key
decreases hi - lo;
{
var mid := (lo + hi) / 2;
if key < a[mid]
{
hi := mid;
}
else if key > a[mid]
{
lo := mid + 1;
}
else
{
return mid;
}
}
return -1;
}
// tests
method Main()
{
var a := new int[6][1,2,3,4,5,6];
var r := binarySearch(a, 5);
assert r == 4; // fails
assert r != 4; // also fails
}
Is this a bug or am I missing something?
Dafny uses post-condition of method to reason about result of method call.
Here post-condition is
If r is between 0 to array length, element at r is equal to key
If r is less than 0, it is not in array.
Dafny doesn't know which of these is vacuous, but you can hint it.
Guarding assert r == 4 with if r >= 0 && r < a.Length will make it verify.
Alternatively after adding assert a[4] == 5 before assert r == 4, verification will go through.
Reason for strange error is after call to binary search, these are facts known to dafny
assert (r < 0) || (0 <= r < 6)
assert (r < 0) ==> forall i :: 0 <= i < a.Length ==> a[i] != 5
assert (0 <= r < 6) ==> a[r] == 5
Using these it can neither prove r == 4 nor r != 4. Dafny doesn't seem to propagate information like assert a[0] == 1 etc by default to prover, you have to explicitly ask for it.
I am trying to create a Dafny program that returns true if and only if, A contains no duplicates.
This is what I have so far, however the invariant invariant r <==> (forall j :: 0 <= j < i && j != i ==> A[j] != A[i]); says that it will not hold on entry.
Any advice on what I am doing wrong?
`method CheckArr1(A: array<int>) returns (r: bool)
requires A.Length > 0
ensures r <==> (forall i, j :: 0 <= i < A.Length && 0 <= j < A.Length && i != j ==> A[i] != A[j]);
{
var i := 0;
r := true;
while i < A.Length
decreases A.Length - i;
invariant i <= A.Length;
invariant r <==> (forall x, y :: 0 <= x < i && 0 <= y < i && x != y ==> A[x] != A[y]);
{
var j := 0;
while j < i
decreases i - j;
invariant j <= i;
invariant r <==> (forall j :: 0 <= j < i && j != i ==> A[j] != A[i]);
{
r := (r && (A[j] != A[i]));
j := j + 1;
}
i := i + 1;
}
}`
The "invariant doesn't hold on entry" error is for the declared invariant
r <==> (forall j :: 0 <= j < i && j != i ==> A[j] != A[i])
of the inner loop. On entry to the that loop, j is 0, so the condition that needs to hold on entry to the inner loop is
r <==> (0 <= 0 < i && 0 != i ==> A[0] != A[i])
which we can simplify to
r <==> (0 < i ==> A[0] != A[i]) // (*)
There is no reason to believe that r will hold this value on entry to the inner loop. All you know inside the body of the outer loop is that
r <==> (forall x, y :: 0 <= x < i && 0 <= y < i && x != y ==> A[x] != A[y]) // (**)
which says that r tells you whether or not there are any duplicates within the first i elements. Condition (*) says something about a[i], whereas (**) does not say anything about a[i].
From your current program, it would be easier if you changed the inner loop to use a different variable, say s, to achieve the invariant you have given. That is, use the invariant
s <==> (forall j :: 0 <= j < i ==> A[j] != A[i])
Then, after the inner loop, update r using the value you computed for s.