|
| 1 | +---------------------------------------------------- |
| 2 | +-- Example 1: Velvet basics |
| 3 | +---------------------------------------------------- |
| 4 | + |
| 5 | +import Mathlib.Algebra.BigOperators.Intervals |
| 6 | +import Mathlib.Algebra.Ring.Int.Defs |
| 7 | + |
| 8 | +import CaseStudies.Velvet.Std |
| 9 | +import CaseStudies.TestingUtil |
| 10 | + |
| 11 | +section squareRoot |
| 12 | + |
| 13 | +set_option loom.semantics.termination "partial" |
| 14 | +set_option loom.semantics.choice "demonic" |
| 15 | +set_option loom.solver "cvc5" |
| 16 | + |
| 17 | +-- (1) Proving things with SMT |
| 18 | +-- partial correctness version of square root |
| 19 | +method sqrt (x: ℕ) return (res: ℕ) |
| 20 | + ensures res * res ≤ x |
| 21 | + ensures ∀ i, i ≤ res → i * i ≤ x |
| 22 | + ensures ∀ i, i * i ≤ x → i ≤ res |
| 23 | + do |
| 24 | + if x = 0 then |
| 25 | + return 0 |
| 26 | + else |
| 27 | + let mut i := 0 |
| 28 | + while i * i ≤ x |
| 29 | + invariant ∀ j, j < i → j * j ≤ x |
| 30 | + do |
| 31 | + i := i + 1 |
| 32 | + return i - 1 |
| 33 | +prove_correct sqrt by |
| 34 | + loom_solve |
| 35 | + -- all_goals loom_smt [*] |
| 36 | + |
| 37 | +end squareRoot |
| 38 | + |
| 39 | +-- (2) Insertion sort |
| 40 | +section insertionSort |
| 41 | + |
| 42 | +/- |
| 43 | +
|
| 44 | +Dafny code below for reference |
| 45 | +
|
| 46 | +method insertionSort(arr: array<int>) |
| 47 | + modifies arr |
| 48 | + ensures forall i, j :: 0 <= i < j < arr.Length ==> arr[i] <= arr[j] |
| 49 | + ensures multiset(arr[..]) == old(multiset(arr[..])) |
| 50 | +{ |
| 51 | + if arr.Length <= 1 { |
| 52 | + return; |
| 53 | + } |
| 54 | + var n := 1; |
| 55 | + while n != arr.Length |
| 56 | + invariant 0 <= n <= arr.Length |
| 57 | + invariant forall i, j :: 0 <= i < j <= n - 1 ==> arr[i] <= arr[j] |
| 58 | + invariant multiset(arr[..]) == old(multiset(arr[..])) |
| 59 | + { |
| 60 | + var mind := n; |
| 61 | + while mind != 0 |
| 62 | + invariant 0 <= mind <= n |
| 63 | + invariant multiset(arr[..]) == old(multiset(arr[..])) |
| 64 | + invariant forall i, j :: 0 <= i < j <= n && (j != mind)==> arr[i] <= arr[j] |
| 65 | + { |
| 66 | + if arr[mind] <= arr[mind - 1] { |
| 67 | + arr[mind], arr[mind - 1] := arr[mind - 1], arr[mind]; |
| 68 | + } |
| 69 | + mind := mind - 1; |
| 70 | + } |
| 71 | + n := n + 1; |
| 72 | + } |
| 73 | +} |
| 74 | +-/ |
| 75 | + |
| 76 | +attribute [grind] Array.multiset_swap |
| 77 | + |
| 78 | + |
| 79 | +set_option loom.semantics.termination "partial" |
| 80 | +-- set_option loom.semantics.termination "total" |
| 81 | +set_option loom.semantics.choice "demonic" |
| 82 | + |
| 83 | +--partial correctness version of insertionSort |
| 84 | +-- uncomment termination measures for total correctness |
| 85 | +method insertionSort |
| 86 | + (mut arr: Array Int) return (u: Unit) |
| 87 | + require 1 ≤ arr.size |
| 88 | + ensures forall i j, 0 ≤ i ∧ i ≤ j ∧ j < arr.size → arr[i]! ≤ arr[j]! |
| 89 | + ensures arr.toMultiset = arr.toMultiset |
| 90 | + do |
| 91 | + let arr₀ := arr |
| 92 | + let arr_size := arr.size |
| 93 | + let mut n := 1 |
| 94 | + while n ≠ arr.size |
| 95 | + invariant arr.size = arr_size |
| 96 | + invariant 1 ≤ n ∧ n ≤ arr.size |
| 97 | + invariant forall i j, 0 ≤ i ∧ i < j ∧ j <= n - 1 → arr[i]! ≤ arr[j]! |
| 98 | + invariant arr.toMultiset = arr₀.toMultiset |
| 99 | + -- decreasing size arr - n |
| 100 | + do |
| 101 | + let mut mind := n |
| 102 | + while mind ≠ 0 |
| 103 | + invariant arr.size = arr_size |
| 104 | + invariant mind ≤ n |
| 105 | + invariant forall i j, 0 ≤ i ∧ i < j ∧ j ≤ n ∧ j ≠ mind → arr[i]! ≤ arr[j]! |
| 106 | + invariant arr.toMultiset = arr₀.toMultiset |
| 107 | + -- decreasing mind |
| 108 | + do |
| 109 | + if arr[mind]! < arr[mind - 1]! then |
| 110 | + swap! arr[mind - 1]! arr[mind]! |
| 111 | + mind := mind - 1 |
| 112 | + -- n := n + 1 -- try commenting this out for termination |
| 113 | + return |
| 114 | + |
| 115 | +-- Let's test this stuff |
| 116 | +extract_program_for insertionSort |
| 117 | + |
| 118 | +-- Determinising the execution |
| 119 | +-- #print insertionSortExec |
| 120 | + |
| 121 | +-- Prove decidability as we need to check them |
| 122 | +prove_precondition_decidable_for insertionSort |
| 123 | +-- #print insertionSortPreDecidable |
| 124 | +prove_postcondition_decidable_for insertionSort |
| 125 | +derive_tester_for insertionSort |
| 126 | + |
| 127 | +-- (2.1) Doing simple testing |
| 128 | +-- See here where the postcondition is violated |
| 129 | + |
| 130 | +-- run_elab do |
| 131 | +-- -- Generate random arrays |
| 132 | +-- let g : Plausible.Gen (_ × Bool) := do |
| 133 | +-- let arr ← Plausible.SampleableExt.interpSample (Array Int) |
| 134 | +-- let res := insertionSortTester arr |
| 135 | +-- pure (arr, res) |
| 136 | +-- for _ in [1: 500] do |
| 137 | +-- let res ← Plausible.Gen.run g 10 |
| 138 | +-- unless res.2 do |
| 139 | +-- IO.println s!"postcondition violated for input {res.1}" |
| 140 | +-- break |
| 141 | + |
| 142 | +-- (2.2) This just works |
| 143 | + |
| 144 | +set_option maxHeartbeats 1000000 |
| 145 | + |
| 146 | +prove_correct insertionSort by |
| 147 | + -- loom_solve |
| 148 | + loom_solve_async |
| 149 | + |
| 150 | +end insertionSort |
0 commit comments