Skip to content

Commit 22f85e8

Browse files
committed
add examples for tutuorial
1 parent 2c7dbe7 commit 22f85e8

File tree

6 files changed

+1236
-0
lines changed

6 files changed

+1236
-0
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
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
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
----------------------------------------------------
2+
-- Example 2: Combining Velvet and non-trivial math
3+
----------------------------------------------------
4+
5+
import Auto
6+
7+
import CaseStudies.Velvet.Std
8+
import CaseStudies.TestingUtil
9+
10+
import Mathlib.Tactic
11+
12+
set_option loom.semantics.termination "total"
13+
set_option loom.semantics.choice "demonic"
14+
15+
open Filter Topology
16+
17+
/- noncomputable is needed because we use operations on ℝ -/
18+
-- This function computers a sum of series
19+
noncomputable method oneThird (n: Nat) return (res: ℝ)
20+
require n > 0
21+
ensures res = ∑ j ∈ Finset.range n, (j^2/n^3 : ℝ)
22+
do
23+
let mut res : Real := 0
24+
let mut i := 0
25+
while i < n
26+
invariant i <= n
27+
invariant res = ∑ j ∈ Finset.range i, (j^2/n^3 : ℝ)
28+
decreasing n - i
29+
do
30+
let x : ℝ := i / n
31+
res := res + (x * x) / n
32+
i := i + 1
33+
return res
34+
35+
grind_pattern Finset.sum_range_succ => Finset.sum (Finset.range (n + 1)) f
36+
grind_pattern Finset.sum_range_zero => Finset.sum (Finset.range 0) f
37+
38+
/- half a second! -/
39+
prove_correct oneThird by loom_solve
40+
41+
-- Proving the closed form for the sum of series: (n * (n + 1) * (2 * n + 1)) / 6
42+
theorem tends_to_third' :
43+
Tendsto (fun n ↦ ∑ i ∈ Finset.range (n + 1), (Nat.cast (R := ℝ) i) ^ 2 / (n + 1) ^ 3) atTop (𝓝 (1 / 3)) := by
44+
-- We'll use the fact that $\sum_{i=0}^{n} i^2 = \frac{n(n+1)(2n+1)}{6}$ to simplify the expression.
45+
have h_sum : ∀ n : ℕ, (∑ i ∈ Finset.range (n + 1), (i : ℝ) ^ 2) = (n * (n + 1) * (2 * n + 1) : ℝ) / 6 := by
46+
exact fun n => by induction n <;> norm_num [ Finset.sum_range_succ ] at * ; linarith;
47+
-- Substitute the formula for the sum of squares into the expression.
48+
suffices h_suff : Filter.Tendsto (fun n : ℕ => ((n * (n + 1) * (2 * n + 1) : ℝ) / 6) / ((n + 1) ^ 3)) Filter.atTop (𝓝 (1 / 3)) by
49+
-- Substitute the formula for the sum of squares into the expression and simplify.
50+
simp_all +decide [ ← Finset.sum_div ];
51+
-- We can divide the numerator and the denominator by $n^3$ and then take the limit as $n$ approaches infinity.
52+
suffices h_div : Filter.Tendsto (fun n : ℕ => ((1 : ℝ) * (1 + 1 / (n : ℝ)) * (2 + 1 / (n : ℝ)) / 6) / ((1 + 1 / (n : ℝ)) ^ 3)) Filter.atTop (𝓝 (1 / 3)) by
53+
refine h_div.congr' ( by filter_upwards [ Filter.eventually_gt_atTop 0 ] with n hn; rw [ div_eq_div_iff ] <;> first | positivity | field_simp [ -one_div, hn.ne' ] );
54+
convert Filter.Tendsto.div ( Filter.Tendsto.div_const ( Filter.Tendsto.mul ( tendsto_const_nhds.mul ( tendsto_const_nhds.add ( tendsto_one_div_atTop_nhds_zero_nat ) ) ) ( tendsto_const_nhds.add ( tendsto_one_div_atTop_nhds_zero_nat ) ) ) _ ) ( Filter.Tendsto.pow ( tendsto_const_nhds.add ( tendsto_one_div_atTop_nhds_zero_nat ) ) _ ) _ using 2 <;> norm_num
55+
56+
-- Proving that (n * (n + 1) * (2 * n + 1)) / 6 tends to 1/3 when n goes to
57+
-- infinity
58+
--
59+
/- main theorem: `oneThird (n + 1) |>.extract` is pure extraction of the `oneThird`
60+
Such extraction is guaranteed to converge and obey the same specification as
61+
the original `oneThird` as we have proven _total correctness_ of the `oneThird` method -/
62+
lemma tends_to_third :
63+
Tendsto (fun n ↦ oneThird (n + 1) |>.extract) atTop (𝓝 (1 / 3)) := by
64+
apply Tendsto.congr _ tends_to_third'; intro n;
65+
erw [VelvetM.extract_spec _ _ (oneThird_correct _)] <;> grind

0 commit comments

Comments
 (0)