3 {1 VerifyThis @ ETAPS 2021 competition
4 Challenge 3: Shearsort}
5 See https://www.pm.inf.ethz.ch/research/verifythis.html
8 - Jean-Christophe FilliĆ¢tre (CNRS)
9 - Andrei Paskevich (Univ. Paris-Saclay)
13 - sequential code only (no parallelism in Why3)
15 - termination verified under some assumptions over the number of
16 inversions in functions `sort_row` and `sort_column`
20 We were not able to show that ceil(log(n))+1 steps suffices.
22 Therefore, we replaced the `repeat` loop infinite loop from which
23 we exit as soon as none of the calls to `sort_column` makes a
26 We argue that this does not increase the complexity. Indeed, it can be
27 proved, independently, that the loops will never execute more than
28 ceil(log(n))+1 steps. Actually, our change even *improves* the
29 performance of the code, as we may perform fewer steps in some cases
30 (e.g. when the matrix is already snake-sorted).
35 use int.ComputerDivision
41 meta coercion function elts
43 (*** SPECIFICATION STARTS HERE ********************************************)
45 function mocc (x: int) (m: matrix int) : int =
46 sum (fun i -> occ x (m i) 0 m.columns) 0 m.rows
48 predicate lt (i j k l: int)
50 i = k /\ if mod i 2 = 0 then j < l else l < j
52 predicate snake_order (m: matrix int)
56 0 <= i < rw -> 0 <= j < cl ->
57 0 <= k < rw -> 0 <= l < cl ->
58 lt i j k l -> m i j <= m k l
60 function inversions (m: matrix int) : int
64 numof (fun l -> lt i j k l /\ m i j > m k l) 0 m.columns)
69 (*** SPECIFICATION STOPS HERE **********************************************)
70 (*** beyond this point, the only thing you need to read are the contracts
71 for functions `sort_row`, `sort_column`, `shearsort` below *)
73 let lemma inv_nonneg (m: matrix int)
74 ensures { 0 <= inversions m }
75 = let zero = pure { fun (_:int) -> 0 } in
76 assert { forall i j k.
77 zero k <= numof (fun l -> lt i j k l /\ m i j > m k l) 0 m.columns };
79 zero j <= sum (fun k ->
80 numof (fun l -> lt i j k l /\ m i j > m k l) 0 m.columns)
83 zero i <= sum (fun j -> sum (fun k ->
84 numof (fun l -> lt i j k l /\ m i j > m k l) 0 m.columns)
87 assert { 0 = sum zero 0 m.rows <= inversions m };
90 predicate sorted_row (m: matrix int) (row: int) (ascending: bool) =
93 forall j l. 0 <= j <= l < m.columns -> m row j <= m row l
95 forall j l. 0 <= j <= l < m.columns -> m row j >= m row l
97 val sort_row (m: matrix int) (row: int) (ascending: bool) : unit
98 requires { 0 <= row < m.rows }
100 ensures { (* frame *)
101 forall i j. 0 <= i < m.rows -> 0 <= j < m.columns ->
102 i <> row -> m i j = old m i j }
103 ensures { forall x. mocc x m = mocc x (old m) }
104 ensures { sorted_row m row ascending }
105 ensures { inversions m <= old inversions m }
107 predicate sorted_column (m: matrix int) (column: int) =
108 0 <= column < m.columns /\
109 forall i k. 0 <= i <= k < m.rows -> m i column <= m k column
111 val sort_column (m: matrix int) (column: int) : (nochange: bool)
112 requires { 0 <= column < m.columns }
114 ensures { (* frame *)
115 forall i j. 0 <= i < m.rows -> 0 <= j < m.columns ->
116 j <> column -> m i j = old m i j }
117 ensures { forall x. mocc x m = mocc x (old m) }
118 ensures { nochange -> forall i. 0 <= i < m.rows ->
119 m i column = old m i column }
120 ensures { sorted_column m column }
121 ensures { inversions m <= old inversions m }
122 ensures { not nochange -> inversions m < old inversions m }
124 let shearsort (n: int) (m: matrix int) : unit
125 requires { m.rows = m.columns = n }
128 ensures { snake_order m }
130 ensures { forall x. mocc x m = mocc x (old m) }
132 while true do (* <-- CHANGE instead of ceil(log(n))+1 steps *)
133 invariant { forall x. mocc x m = mocc x (old m) }
134 variant { inversions m }
136 for i = 0 to n - 1 do
137 invariant { forall k. 0 <= k < i -> sorted_row m k (mod k 2 = 0) }
138 invariant { forall x. mocc x m = mocc x (old m) }
139 invariant { inversions m <= (inversions m at L1) }
140 sort_row m i (mod i 2 = 0)
143 let ref nochange = true in
144 for j = 0 to n - 1 do
145 invariant { nochange ->
146 forall i j. 0 <= i < m.rows -> 0 <= j < m.columns ->
147 m i j = (m i j at L2) }
148 invariant { forall l. 0 <= l < j -> sorted_column m l }
149 invariant { forall x. mocc x m = mocc x (old m) }
150 invariant { inversions m <= (inversions m at L2) }
151 invariant { not nochange -> inversions m < (inversions m at L2) }
152 let nch = sort_column m j in
153 if not nch then nochange <- false
155 if nochange then break
157 let lemma ordered (i j k l: int)
158 requires { 0 <= i < n } requires { 0 <= j < n }
159 requires { 0 <= k < n } requires { 0 <= l < n }
160 requires { lt i j k l } ensures { m i j <= m k l }
161 = if i = k then return;
162 assert { sorted_column m j };
163 assert { sorted_column m l };
166 assert { m i j <= m i l <= m k l }
168 assert { mod (i+1) 2 = 0 };
169 assert { sorted_row m (i+1) true };
170 assert { m i j <= m (i+1) j <= m (i+1) l <= m k l }
178 The verification tasks for shearsort are:
180 1. Verify that shearsort terminates, and is memory safe.
181 PARTIAL: memory safety DONE (enforced by Why3)
182 termination using hypotheses over number of inversions
184 2. Verify that shearsort permutes the input matrix.
187 3. Verify that shearsort sorts the matrix in a snake-like manner.
190 4. Verify that (parallel) shearsort satisfies the same specification
191 as sequenti al shearsort, in which all parallel for-loops are replaced
194 5. Verify that shearsort and alternative-shearsort satisfy the same
197 6. Extra: give implementations to sort-row, sort-column and transpose,
198 and verify these as well.