In Dafny I am trying to make a Max polymorphic and high-order function that, given a sequence and a predicate, returns the longest subsequence that holds it. For instance, the longest increasing subsequence, or the longest subsequence in which all the elements are zero.
To do so, I designed a slow algorithm (given the P predicate and a S sequence):
1. Start an i pivot in the left and a j pivot in the same place.
2. Start the max_sequence = [] and the max_sequence_length = 0.
3. While i<S.length:
counter = 0
j = i
While max_sequence[i..j] satisfies P and j<S.length:
If counter > max_sequence_length:
max_sequence_length = counter
max_sequence = max_sequence[i..j]
Increment j
Increment i
4. Return max_sequence
You can see it implemented:
method maxPropertySequence<T>(P: seq<T> -> bool, sequ: seq<T>) returns (max_seq: seq<T>)
{
var i := 0;
var j := 0;
var longest := 0;
var the_sgmt := sequ;
var fresh_segmnt := sequ;
var counter := longest;
while i<(|sequ|)
decreases |sequ|-i
{
j := i;
counter := 0;
fresh_segmnt := [sequ[i]];
if P(fresh_segmnt)
{
j := j+1;
counter:=counter+1;
if counter>longest {
longest:=counter;
the_sgmt := fresh_segmnt;
}
while P(fresh_segmnt) && j<|sequ|
decreases |sequ|-j
{
fresh_segmnt := fresh_segmnt + [sequ[j]];
j := j+1;
counter:=counter+1;
if counter>longest {
longest:=counter;
the_sgmt := fresh_segmnt;
}
}
}
i := i+1;
}
return the_sgmt;
}
My question is: how can I verify that the Max function behaves as I expect? More concretely: which are the ensures I have to add?
I have thought something like: forall the subsequences of the original sequence, there is no subsequence that holds P and is longer than the_sgmt. But I do not know how to express it efficiently.
Thanks!
I wrote code for finding the (leftmost) longest subsequence of zeros from a given integer array. Since you can map sequ using the predicate, these two are almost identical problems.
// For a given integer array, let's find the longest subesquence of 0s.
// sz: size, pos: position. a[pos..(pos+sz)] will be all zeros
method longestZero(a: array<int>) returns (sz:int, pos:int)
requires 1 <= a.Length
ensures 0 <= sz <= a.Length
ensures 0 <= pos < a.Length
ensures pos + sz <= a.Length
ensures forall i:int :: pos <= i < pos + sz ==> a[i] == 0
ensures forall i,j :: (0 <= i < j < a.Length && getSize(i, j) > sz) ==> exists k :: i <= k <= j && a[k] != 0
{
var b := new int[a.Length]; // if b[i] == n, then a[i], a[i-1], ... a[i-n+1] will be all zeros and (i-n ==0 or a[i-n] !=0)
if a[0] == 0
{b[0] := 1;}
else
{b[0] := 0;}
var idx:int := 0;
while idx < a.Length - 1 // idx <- 0 to a.Length - 2
invariant 0 <= idx <= a.Length - 1
invariant forall i:int :: 0 <= i <= idx ==> 0 <= b[i] <= a.Length
invariant forall i:int :: 0 <= i <= idx ==> -1 <= i - b[i]
invariant forall i:int :: 0 <= i <= idx ==> (forall j:int :: i-b[i] < j <= i ==> a[j] == 0)
invariant forall i:int :: 0 <= i <= idx ==> ( 0 <= i - b[i] ==> a[i - b[i]] != 0 )
{
if a[idx + 1] == 0
{ b[idx + 1] := b[idx] + 1; }
else
{ b[idx + 1] := 0;}
idx := idx + 1;
}
idx := 1;
sz := b[0];
pos := 0;
// Let's find maximum of array b. That is the desired sz.
while idx < a.Length
invariant 1 <= idx <= b.Length
invariant 0 <= sz <= a.Length
invariant 0 <= pos < a.Length
invariant pos + sz <= a.Length
invariant forall i:int :: 0 <= i < idx ==> b[i] <= sz
invariant forall i:int :: pos <= i < pos + sz ==> a[i] == 0
invariant forall i, j:int :: (0 <= i < j < idx && getSize(i,j) > sz) ==> a[j-b[j]] != 0
{
// find max
if b[idx] > sz
{
sz := b[idx];
pos := idx - b[idx] + 1;
}
idx := idx + 1;
}
}
function getSize(i: int, j:int) : int
{
j - i + 1
}
Since I am new to dafny, any comments on style or anything are appreciated.
Related
Good day to all! I wrote the Shell sort verification code, but I can’t build the correct loop invariants.It is not possible to correctly compose invariants and prove the correctness of the program... Please help me!
/*# predicate Sorted{L}(int* a, integer m, integer n) =
# \forall integer i, j; m <= i <= j < n ==> a[i] <= a[j];
*/
/*# predicate GapSorted(int* a, integer m, integer n, integer gap) =
# \forall integer i, j; (m <= i <= j < n && j % gap == i % gap) ==> a[i] <=a[j];
*/
/*#
# requires \valid(arr + (0..n-1));
# requires n > 1;
# ensures GapSorted(arr, 0, n, 1);
*/
void shell_lr(int *arr, int n) {
int i, j, tmp, gap;
/*# ghost int gap1 = n
# loop invariant 0 <= gap1 <= n/2;
# loop invariant gap1 < n/2 ==> GapSorted(arr, 0, n, gap+1);
# //loop invariant \forall integer k; gap < k <= n/2 ==> GapSorted(arr, 0, n, k);
# loop variant gap1;
*/
for (gap = n / 2; gap > 0; gap--) {
/*# loop invariant 0 <= i <= n;
# //loop invariant \forall integer m; gap < m <= n/2 ==> GapSorted(arr, 0, i, m);
# loop invariant GapSorted(arr, 0, i, gap);
# loop variant n - i; */
for (i = gap; i < n; i++) {
tmp = arr[i];
/*#
# loop invariant 0 <= j <= i;
# //loop invariant arr[j] >= tmp;
# loop invariant \forall integer k; (j < k <= i) ==> GapSorted(arr, 0, i, k);
#// loop invariant \forall integer k; j <= k <= gap ==> GapSorted(arr, k, i, gap);
# loop variant j;
#*/
for (j = i; j >= gap && arr[j - gap] > tmp; j -= gap) {
arr[j] = arr[j - gap];
//# assert arr[j] >= arr[j - gap];
//# assert tmp < arr[j - gap];
}
//# assert j>=0;
arr[j] = tmp;
}
//# assert i == n;
//# assert GapSorted(arr, 0, i, gap);
//# assert gap > 0;
// assert GapSorted(arr, 0, n, gap);
}
First, the code you provided is not syntactically correct:
a brace closing the function's body is missing
the ghost declaration of gap1 cannot be mixed with the loop annotations. This should be two distinct annotations. Anyway, I don't see the use of gap1 (especially as it is not updated inside the loop), everything can be expressed with gap. If what you were trying to achieve was to have a local variable for the whole loop annotation, this is not possible in ACSL: you have the \let gap1 = ...; ... construction, but its scope is only a single term/predicate: you can't share it with across two loop invariants (or loop invariants and loop variant)
Now, your most pressing issue here is the lack of loop assigns in your loop annotations. You must provide such clauses for all your loops, or WP will not be able to assume much about the state of the program after the loops (see for instance this answer for more detail). You might also want to strengthen a bit your invariant on i in the middle loop as gap <= i <= n, but this is a detail.
With the following loop assigns, most of your annotations gets proved (Frama-C 20.0 Calcium with -wp -wp-rte).
/*# predicate Sorted{L}(int* a, integer m, integer n) =
# \forall integer i, j; m <= i <= j < n ==> a[i] <= a[j];
*/
/*# predicate GapSorted(int* a, integer m, integer n, integer gap) =
# \forall integer i, j; (m <= i <= j < n && j % gap == i % gap) ==> a[i] <=a[j];
*/
/*#
# requires \valid(arr + (0..n-1));
# requires n > 1;
# ensures GapSorted(arr, 0, n, 1);
*/
void shell_lr(int *arr, int n) {
int i, j, tmp, gap;
/*#
# loop invariant 0 <= gap <= n/2;
# loop invariant gap < n/2 ==> GapSorted(arr, 0, n, gap+1);
# loop assigns gap, i, j, tmp, arr[0 .. n - 1];
# //loop invariant \forall integer k; gap < k <= n/2 ==> GapSorted(arr, 0, n, k);
# loop variant gap;
*/
for (gap = n / 2; gap > 0; gap--) {
/*# loop invariant gap <= i <= n;
# //loop invariant \forall integer m; gap < m <= n/2 ==> GapSorted(arr, 0, i, m);
# loop invariant GapSorted(arr, 0, i, gap);
loop assigns i,j,tmp,arr[0..n-1];
# loop variant n - i; */
for (i = gap; i < n; i++) {
tmp = arr[i];
/*#
# loop invariant 0 <= j <= i;
# //loop invariant arr[j] >= tmp;
# loop invariant \forall integer k; (j < k <= i) ==> GapSorted(arr, 0, i, k);
#// loop invariant \forall integer k; j <= k <= gap ==> GapSorted(arr, k, i, gap);
loop assigns j, arr[gap .. i];
# loop variant j;
#*/
for (j = i; j >= gap && arr[j - gap] > tmp; j -= gap) {
arr[j] = arr[j - gap];
//# assert arr[j] >= arr[j - gap];
//# assert tmp < arr[j - gap];
}
//# assert j>=0;
arr[j] = tmp;
}
//# assert i == n;
//# assert GapSorted(arr, 0, i, gap);
//# assert gap > 0;
// assert GapSorted(arr, 0, n, gap);
}
}
What remains to be proved are the GapSorted invariants for the two inner loops, which probably require more work and an answer much longer than what is fit for this format.
This is the algorithm:
// Precondition: n > 0
l = -1;
r = n;
while (l+1 != r) {
m = (l+r)/2;
// I && m == (l+r)/2
if (a[m] <= x) {
l = m;
} else {
r = m;
}
}
// Postcondition: -1 <= l < n
I have done some research and narrowed the invariant down to if x is in a[0 .. n-1] then a[l] <= x < a[r].
I have no idea how to progress from there though. The precondition seems too broad, so I'm having trouble showing that P -> I.
Any help is extremely appreciated. These are the logic rules that can be used to prove the algorithm's correctness:
The invariant is
-1 <= l and l + 1 < r <= n and a[l] <= x < a[r]
with the implicit convention a[-1] = -∞, a[n] = +∞.
Then in the if statement
a[l] <= x < a[r] and a[m] <= x implies a[m] <= x < a[r]
and
a[l] <= x < a[r] and x < a[m] implies a[l] <= x < a[m].
In either case, the assignment establishes a[l] <= x < a[r].
At the same time, -1 <= l and l + 1 < r <= n ensures -1 < m < n, so that the evaluation of a[m] is possible.
Upon termination, l + 1 = r and by the invariant
-1 <= l < n and a[l] <= x < a[l + 1].
I read the article about
How to determine the longest increasing sub-sequence using dynamic programming
with this algorithm:
int maxLength = 1, bestEnd = 0;
DP[0] = 1;
prev[0] = -1;
for (int i = 1; i < N; i++)
{
DP[i] = 1;
prev[i] = -1;
for (int j = i - 1; j >= 0; j--)
if (DP[j] + 1 > DP[i] && array[j] < array[i])
{
DP[i] = DP[j] + 1;
prev[i] = j;
}
if (DP[i] > maxLength)
{
bestEnd = i;
maxLength = DP[i];
}
}
but i want to know how to solve this problem with this condition that we can take the arrays with joined integers.
For example: 1,5,3,1,5,6,7,8,1,2,9
we can have this set:1,3,5,6,7,8,12 for solution
that 12 is joint form 1 and 2
so conditions are:
The input array includes 1-9 numbers! and the integers can joined from few other integers.
Original problem
dp[i] = max(DP[j] + 1, a[j] < a[i])
Your problem
Let:
a[x,y] = a[x] + a[x + 1] + ... + a[y] (+ means concatenate)
So:
f[x,y] = max(DP[j] + 1, a[j] < a[x,y], j < x)
dp[i] = max(f[i,j], 0 <= j <= i) = max(
max(DP[j] + 1, a[j] < a[i], j < i) # f(i, i)
max(DP[j] + 1, a[j] < a[i-1, i], j < i - 1) # f(i-1, i)
...
)
If you still have some problems, please don't hesitate to leave a comment here.
i am new in Dafny and i have problems verifying my insertionSort-implementation.
Dafny tells me the the multiset invariants are not holding, anything else is working fine. After Hours of searching the mistake i could need some help :)
Would be nice if somebody could tell me the trick!
My Code:
predicate sorted(a:array<int>, min:int, max:int)
requires a != null;
requires 0<= min <= max <= a.Length;
reads a;
{
forall j, k :: min <= j < k < max ==> a[j] <= a[k]
}
/*
*
*/
method insertionSort(a: array<int>)
requires a != null;
requires a.Length > 0;
ensures sorted(a, 0, a.Length);
ensures multiset(a[..]) == multiset(old(a[..]));
modifies a;
{
var i := 1;
while(i < a.Length)
invariant 1 <= i <= a.Length;
invariant sorted(a, 0, i);
invariant a != null;
invariant multiset(old(a[..])) == multiset(a[..]);
decreases a.Length-i;
{
var j := i - 1;
var key := a[i];
while(j >= 0 && key < a[j])
invariant -1 <= j <= i - 1 <= a.Length;
invariant (j == i-1 && sorted(a, 0, i)) || (sorted(a, 0, i+1));
invariant forall k :: j < k < i ==> a[k] >= key;
invariant -1 < j == i - 1 ==> multiset(old(a[..])) == multiset(a[..]);
invariant |multiset(old(a[..]))| == |multiset(a[..])|;
invariant -1 < j < i - 1 && key < a[j] ==> multiset(old(a[..])) == multiset(a[..]) - multiset({a[j+1]}) + multiset({key});
invariant -1 == j ==> multiset(old(a[..])) == multiset(a[..]) + multiset({key}) - multiset({a[j+1]});
decreases j;
{
a[j + 1] := a[j];
j := j - 1;
}
a[j + 1] := key;
i := i + 1;
}
}
It produces
1 This loop invariant might not be maintained by the loop. 29,38
2 This loop invariant might not be maintained by the loop. 42,73
3 This loop invariant might not be maintained by the loop. 43,52
Link: http://rise4fun.com/Dafny/3R5
Here is a slightly modified algorithm that does verify. It shifts the elements up by doing a swap. I think with a bit more work it could be adapted to your algorithm. It just needs a slightly more complex multiset invariant (which would require a lemma about adding and removing things from multisets).
predicate sorted(a:array<int>, min:int, max:int)
requires a != null;
requires 0<= min <= max <= a.Length;
reads a;
{
forall j, k :: min <= j < k < max ==> a[j] <= a[k]
}
predicate sortedSeq(a:seq<int>)
{
forall j, k :: 0 <= j < k < |a| ==> a[j] <= a[k]
}
lemma sortedSeqSubsequenceSorted(a:seq<int>, min:int, max:int)
requires 0<= min <= max <= |a|
requires sortedSeq(a)
ensures sortedSeq(a[min .. max])
{ }
method swap(a: array<int>, i:int, j:int)
modifies a;
requires a != null && 0 <= i < j < a.Length
requires i + 1 == j
ensures a[..i] == old(a[..i])
ensures a[j+1..] == old(a[j+1..])
ensures a[j] == old(a[i])
ensures a[i] == old(a[j])
ensures multiset(a[..]) == multiset(old(a[..]))
{
var tmp := a[i];
a[i] := a[j];
a[j] := tmp;
}
method insertionSort(a: array<int>)
modifies a;
requires a != null;
requires a.Length > 0;
ensures sorted(a, 0, a.Length);
ensures multiset(a[..]) == multiset(old(a[..]));
{
var i := 0;
while(i < a.Length)
invariant 0 <= i <= a.Length
invariant sorted(a, 0, i)
invariant multiset(old(a[..])) == multiset(a[..]);
{
var key := a[i];
var j := i - 1;
ghost var a' := a[..];
assert sortedSeq(a'[0..i]);
while(j >= 0 && key < a[j])
invariant -1 <= j <= i - 1
invariant a[0 .. j+1] == a'[0 .. j+1]
invariant sorted(a, 0, j+1);
invariant a[j+1] == key == a'[i];
invariant a[j+2 .. i+1] == a'[j+1 .. i]
invariant sorted(a, j+2, i+1);
invariant multiset(old(a[..])) == multiset(a[..])
invariant forall k :: j+1 < k <= i ==> key < a[k]
{
ghost var a'' := a[..];
swap(a, j, j+1);
assert a[0..j] == a''[0..j];
assert a[0..j] == a'[0..j];
assert a[j] == a''[j+1] == a'[i] == key;
assert a[j+2..] == a''[j+2..];
assert a[j+2..i+1] == a''[j+2..i+1] == a'[j+1..i];
j := j - 1;
sortedSeqSubsequenceSorted(a'[0..i], j+1, i);
assert sortedSeq(a'[j+1..i]);
assert a[j+2 .. i+1] == a'[j+1 .. i];
assert sortedSeq(a[j+2..i+1]);
}
i := i + 1;
}
}
http://rise4fun.com/Dafny/Gplux
This is programming puzzle. We have two arrays A and B. Both contains 0's and 1's only.
We have to two indices i, j such that
a[i] + a[i+1] + .... a[j] = b[i] + b[i+1] + ... b[j].
Also we have to maximize this difference between i and j. Looking for O(n) solution.
I found O(n^2) solution but not getting O(n).
Best solution is O(n)
First let c[i] = a[i] - b[i], then question become find i, j, which sum(c[i], c[i+1], ..., c[j]) = 0, and max j - i.
Second let d[0] = 0, d[i + 1] = d[i] + c[i], i >= 0, then question become find i, j, which d[j + 1] == d[i], and max j - i.
The value of d is in range [-n, n], so we can use following code to find the answer
answer = 0, answer_i = 0, answer_j = 0
sumHash[2n + 1] set to -1
for (x <- 0 to n) {
if (sumHash[d[x]] == -1) {
sumHash[d[x]] = x
} else {
y = sumHash[d[x]]
// find one answer (y, x), compare to current best
if (x - y > answer) {
answer = x - y
answer_i = y
answer_j = y
}
}
}
Here is an O(n) solution.
I use the fact that sum[i..j] = sum[j] - sum[i - 1].
I keep the leftmost position of each found sum.
int convertToPositiveIndex(int index) {
return index + N;
}
int mostLeft[2 * N + 1];
memset(mostLeft, -1, sizeof(mostLeft));
int bestLen = 0, bestStart = -1, bestEnd = -1;
int sumA = 0, sumB = 0;
for (int i = 0; i < N; i++) {
sumA += A[i];
sumB += B[i];
int diff = sumA - sumB;
int diffIndex = convertToPositiveIndex(diff);
if (mostLeft[diffIndex] != -1) {
//we have found the sequence mostLeft[diffIndex] + 1 ... i
//now just compare it with the best one found so far
int currentLen = i - mostLeft[diffIndex];
if (currentLen > bestLen) {
bestLen = currentLen;
bestStart = mostLeft[diffIndex] + 1;
bestEnd = i;
}
}
if (mostLeft[diffIndex] == -1) {
mostLeft[diffIndex] = i;
}
}
cout << bestStart << " " << bestEnd << " " << bestLen << endl;
P.S. mostLeft array is 2 * N + 1, because of the negatives.
This is a fairly straightforward O(N) solution:
let sa = [s1, s2, s3.. sn] where si = sum(a[0:i]) and similar for sb
then sum(a[i:j]) = sa[j]-sa[i]
and sum(b[i:j]) = sb[j] - sb[i]
Note that because the sums only increase by 1 each time, we know 0 <= sb[N], sa[N] <=N
difference_array = [d1, d2, .. dn] where di = sb[i] - sa[i] <= N
note if di = dj, then sb[i] - sa[i] = sb[j] - sa[j] which means they have the same sum (rearrange to get sum(b[i:j]) and sum(a[i:j]) from above).
Now for each difference we need its max position occurrence and min position occurrence
Now for each difference di, the difference between max - min, is an i-j section of equal sum. Find the maximum max-min value and you're done.
sample code that should work:
a = []
b = []
sa = [0]
sb = [0]
for i in a:
sa.append(sa[-1] + i)
for i in b:
sb.append(sb[-1] + i)
diff = [sai-sbi for sai, sbi in zip(sa, sb)]
min_diff_pos = {}
max_diff_pos = {}
for pos, d in enumerate(diff):
if d in min_diff_pos:
max_diff_pos[d] = pos
else:
min_diff_pos[d] = pos
ans = min(max_diff_pos[d] - min_diff_pos[d] for d in diff)
Basically, my solution goes like this.
Take a variable to take care of the difference since the beginning.
int current = 0;
for index from 0 to length
if a[i] == 0 && b[i] == 1
current--;
else if a[i] == 1 && b[i] == 0
current++;
else
// nothing;
Find the positions where the variable has the same value, which indicates that there are equal 1s and 0s in between.
Pseudo Code:
Here is my primary solution:
int length = min (a.length, b.length);
int start[] = {-1 ... -1}; // from -length to length
start[0] = -1;
int count[] = {0 ... 0}; // from -length to length
int current = 0;
for (int i = 0; i < length; i++) {
if (a[i] == 0 && b[i] == 1)
current--;
else if (a[i] == 1 && b[i] == 0)
current++;
else
; // nothing
if (start[current] == -1) // index can go negative here, take care
start[current] = current;
else
count[current] = i - start[current];
}
return max_in(count[]);