Proving insertion sort algorithm using Isabelle - insertion-sort

I did some implementation of the insert sort algorithm in Isabelle/HOL for the generation of code (ML, Python, among others). I'm sure the corresponding functions work fine, but I need to create some theorems to prove it and be super sure it works. My functions are these:
(* The following functions are used to prove the algorithm works fine *)
fun menor_igual :: "nat ⇒ nat list ⇒ bool" where
"menor_igual n [] = True" |
"menor_igual n (x # xs) = (n ≤ x ∧ menor_igual n xs)"
fun ordenado :: "nat list ⇒ bool" where
"ordenado [] = True" |
"ordenado (x # xs) = (menor_igual x xs ∧ ordenado xs)"
fun cuantos :: "nat list ⇒ nat ⇒ nat" where
"cuantos [] num = 0" |
"cuantos (x # xs) num = (if x = num then Suc (cuantos xs num) else cuantos xs num)"
(* These functions make the algorithm itself *)
fun insertar:: "nat ⇒ nat list ⇒ nat list" where
"insertar num [] = [num]" |
"insertar num (x # xs) = (if (num ≤ x) then (num # x # xs) else x # insertar num xs)"
fun asc :: "nat list ⇒ nat list" where
"asc [] = []" |
"asc (x # xs) = insertar x (asc xs)"
The issues is I don't know how to create the theorems correctly. I need to prove that the ordered list has the same length as the original list, and that both lists have the same name of elements. My first theorems are these:
theorem "ordenado (asc xs)"
apply (induction xs rule: asc.induct)
apply auto
theorem "cuantos (asc xs) x = cuantos xs x"
apply (induction xs rule: asc.induct)
apply auto
The first theorem tries to prove that the list is correctly ordered, the second one tries to prove that both lists have the same length.
When I apply induction and auto I expect to get 0 subgoals, that says the theorems are right and the algorithm works fine, but after that I don't know how to remove the subgolas, I mean I don't know how to do the simplification rules (lemma [simp]: "") to do it, I'll appreciate your help.

After
theorem "ordenado (asc xs)"
apply (induction xs rule: asc.induct)
apply auto
you still have to prove the following subgoal:
1. ⋀x xs.
ordenado (asc xs) ⟹
ordenado (insertar x (asc xs))
That is, assuming that asc xs is sorted, you have to prove that insertar x (asc xs) is sorted. This suggests to first prove an auxiliary lemma about the interaction between insertar and ordenado
lemma ordenado_insertar [simp]:
"ordenado (insertar x xs) = ordenado xs"
...
which states that insertar x xs is sorted if and only if xs was already sorted. In order to prove this lemma you will again need auxiliary lemmas. This time about menor_igual and the interaction between menor_igual and insertar.
lemma le_menor_igual [simp]:
"y ≤ x ⟹ menor_igual x xs ⟹ menor_igual y xs"
...
lemma menor_igual_insertar [simp]:
"menor_igual y (insertar x xs) ⟷ y ≤ x ∧ menor_igual y xs"
...
The first one states that if y is smaller-equal x and x is smaller-equal all elements of xs, then y is smaller-equal all elements of xs, etc. ...
I leave the proofs as an exercise ;).
For your second theorem I suggest to follow the same recipe. First try induct followed by auto (as you already did), then find out what properties are still missing, prove them as auxiliary lemmas and finish your proofs.

Related

Lean4: Proving that `(xs = ys) = (reverse xs = reverse ys)`

Intuitively stating that xs is equal to ys is the same as saying that the respective reverse lists are equal to each other.
I'm currently learning Lean 4 and so I set myself the following exercise. I want to prove the following theorem in Lean 4:
theorem rev_eq (xs ys : List α) : (xs = ys) = (reverse xs = reverse ys)
However, I am not sure, whether this theorem can actually be proven in Lean 4. If not, why can it not be proven?
The closest I could get so far is proving the claim under the assumption that xs = ys:
theorem rev_eq' (xs ys : List α) :
xs = ys -> (xs = ys) = (reverse xs = reverse ys) := by
intros h
rw [h]
simp
Maybe, if one could prove that the claim also holds if one assumes that xs is not equal to ys, then the original theorem would follow. I got stuck on that route as well though.
Any ideas?
In Lean, it is usually idiomatic to state the equality of propositions using Iff, which is equivalent under the axiom propext. From there, iff is an inductive type with two sides, one direction that you have proved, and the other direction that follows from induction. (This theorem is still provable without this axiom, fwiw)
But I'd recommend you to do induction xs and induction ys and then look at the goals. Two should be impossible and Lean should show you they are contradictions (or indeed you'll get a that can be simplified to false = false) and two trivially true goals. Make sure to expand the definition of reverse.
This property is typically proved by induction. I never used lean but once induction on the list is done then it should be easy. You may need a lemma saying that (xs = ys) -> xs#l = xs#l (also by induction I guess, # stands for list concatenation).

I'm trying to build a proof in Coq that two different permutation definitions are equivalent, but the non-inductive side is not working

The two definitions are these:
Inductive perm : list nat -> list nat -> Prop :=
| perm_eq: forall l1, perm l1 l1
| perm_swap: forall x y l1, perm (x :: y :: l1) (y :: x :: l1)
| perm_hd: forall x l1 l2, perm l1 l2 -> perm (x :: l1) (x :: l2)
| perm_trans: forall l1 l2 l3, perm l1 l2 -> perm l2 l3 -> perm l1 l3.
Fixpoint num_oc (x: nat) (l: list nat): nat :=
match l with
| nil => 0
| h::tl =>
if (x =? h) then S (num_oc x tl) else num_oc x tl
end.
Definition equiv l l' := forall n:nat, num_oc n l = num_oc n l'.
The theorem that I'm trying to prove is this:
Theorem perm_equiv: forall l l', equiv l l' <-> perm l l'.
The perm -> equiv direction is ready, but the equiv -> perm direction isn't working. I tried this strategy:
- intro H. unfold equiv in H.
generalize dependent l'.
induction l.
+ intros l' H. admit.
+ intros l' H. simpl in H.
generalize dependent l'.
intro l'. induction l'.
* intro H. specialize (H a).
rewrite <- beq_nat_refl in H.
simpl in H. Search False.
inversion H.
destruct (a =? a0) eqn:Ha.
** simpl in H. inversion H.
** apply False_ind.
apply beq_nat_false in Ha.
apply Ha. reflexivity.
* destruct (x =? a). *).
I'm out of ideas for the first branch, so it's admitted for now, but the second one is crashing at the destruct tactic. How do I proceed with this proof?
You should attempt to write a proof on paper before attempting to encode it in Coq. Here is a possible strategy.
Nil case
When l = [], you know that every number in l' occurs zero times because of H. It should be possible to prove an auxiliary lemma that implies that l' = [] in this case. You can conclude with perm_eq.
Cons case
Suppose that l = x :: xs. Let n = num_oc x xs. We know that num_oc x l' = S n by H. You should be able to prove a lemma saying that l' is of the form ys1 ++ x :: ys2 where num_oc x ys1 = 0. This would allow you to show that equiv xs (ys1 ++ ys2). By the induction hypothesis, you find that perm xs (ys1 ++ ys2). Hence, by perm_hd, perm (x :: xs) (x :: ys1 ++ ys2).
You should be able to prove that perm is a transitive relation and that perm (x :: ys1 ++ ys2) (ys1 ++ x :: ys2) holds. Combined with the last assertion, this will yield perm l l'.
The main takeaway in this case is that attempting to write every proof with single, direct induction is only going to work for the simplest results. You should start thinking about how to break down your results into simpler intermediate lemmas that you can combine to prove your final result.

Coq proof that complement is involutive

How can I prove that the complement of a set is involutive?
Require Import Ensembles. Arguments In {_}. Arguments Complement {_}.
Variables (T:Type) (A:Ensemble T).
Axiom set_eq: forall (E1 E2:Ensemble T), (forall x, E1 x <-> E2 x) -> E1 = E2.
Lemma complement_involutive:
forall x, In (Complement (Complement A)) x -> In A x.
EDIT: Assuming decidable (In A x) enables firstorder to prove the lemma completely.
complement_involutive is exactly ~ ~ A x -> A x which is well-known to be equivalent to excluded middle, in this case in Type, thus not provable in Coq without assuming it as an axiom. See this answer https://math.stackexchange.com/questions/1370805/why-cant-you-prove-the-law-of-the-excluded-middle-in-intuitionistic-logic-for

Provably correct permutation in less than O(n^2)

Written in Haskell, here is the data type that proves that one list is a permutation of another:
data Belongs (x :: k) (ys :: [k]) (zs :: [k]) where
BelongsHere :: Belongs x xs (x ': xs)
BelongsThere :: Belongs x xs xys -> Belongs x (y ': xs) (y ': xys)
data Permutation (xs :: [k]) (ys :: [k]) where
PermutationEmpty :: Permutation '[] '[]
PermutationCons :: Belongs x ys xys -> Permutation xs ys -> Permutation (x ': xs) xys
With a Permutation, we can now permute a record:
data Rec :: (u -> *) -> [u] -> * where
RNil :: Rec f '[]
(:&) :: !(f r) -> !(Rec f rs) -> Rec f (r ': rs)
insertRecord :: Belongs x ys zs -> f x -> Rec f ys -> Rec f zs
insertRecord BelongsHere v rs = v :& rs
insertRecord (BelongsThere b) v (r :& rs) = r :& insertRecord b v rs
permute :: Permutation xs ys -> Rec f xs -> Rec f ys
permute PermutationEmpty RNil = RNil
permute (PermutationCons b pnext) (r :& rs) = insertRecord b r (permute pnext rs)
This works fine. However, permute is O(n^2) where n is the length of the record. I'm wondering if there is a way to get it to be any faster by using a different data type to represent a permutation.
For comparison, in a mutable and untyped setting (which I know is a very different setting indeed), we could apply a permutation to a heterogeneous record like this in O(n) time. You represent the record as an array of values and the permutation as an array of new positions (no duplicates are allowed and all digits must be between 0 and n). Applying the permutation is just iterating that array and indexing into the record's array with those positions.
I don't expect that an O(n) permutation is possible in a more rigorously typed settings. But it seems like O(n*log(n)) might be possible. I appreciate any feedback, and let me know if I need to clarify anything. Also, answers to this can use Haskell, Agda, or Idris depending on what it feels easier to communicate with.
A faster simple solution is to compare the sorted permutation of the permutations.
Given permutation A and B.
Then there exist the sorted permutations,
As = sort(A)
Bs = sort(B)
As is a permutation of A and Bs is a permutation of B.
If As == Bs then A is a permutation of B.
Thus the order of this algorithm is O(n log(n)) < O(n²)
And this is leading to the optimal solution.
Using a different storage of permutation yields O(n)
Using the statements from above, we are changing the storage format of each permutation into
the sorted data
the original unsorted data
To determine if a list is a permutation of another one, simple a comparison of the sorted data is necessary -> O(n).
This answers the question correctly, but the effort is hidden in creating the doubled data storage ^^ So it will depend on the use if this is a real advantage or not.

Lazy Evaluation Correctness and Totality (Coq)

As the title suggests, my question concerns proving the correctness and totality of lazy evaluation of arithmetic expressions in Coq. The theorems that I would like to prove are three in total:
Computations only give canonical
expressions as results
Theorem Only_canonical_results:
(forall x y: Aexp, Comp x y -> Canonical y).
Correctness: the computation relation
preserves denotation of expressions
Theorem correct_wrt_semantics:
(forall x y: Aexp, Comp x y ->
I N (denotation x) (denotation y)).
Every input leads to some result.
Theorem Comp_is_total: (forall x:Aexp,
(Sigma Aexp (fun y =>
prod (Comp x y) (Canonical y)))).
The necessary definitions are to be found in the code attached below. I should make it clear I am a novice when it comes to Coq; which more experienced users will probably notice right away. It is most certainly the case that the majority, or perhaps even all of the background material I have written can be found in the standard library. But, then again, if I knew exactly what to import from the standard library in order to prove the desired results, I would most probably not be here bothering you. That is why I submit to you the material I have so far, in the hope that some kind spirited person/s may help me. Thanks!
(* Sigma types *)
Inductive Sigma (A:Set)(B:A -> Set) :Set :=
Spair: forall a:A, forall b : B a,Sigma A B.
Definition E (A:Set)(B:A -> Set)
(C: Sigma A B -> Set)
(c: Sigma A B)
(d: (forall x:A, forall y:B x,
C (Spair A B x y))): C c :=
match c as c0 return (C c0) with
| Spair a b => d a b
end.
Definition project1 (A:Set)(B:A -> Set)(c: Sigma A B):=
E A B (fun z => A) c (fun x y => x).
(* Binary sum type *)
Inductive sum' (A B:Set):Set :=
inl': A -> sum' A B | inr': B -> sum' A B.
Print sum'_rect.
Definition D (A B : Set)(C: sum' A B -> Set)
(c: sum' A B)
(d: (forall x:A, C (inl' A B x)))
(e: (forall y:B, C (inr' A B y))): C c :=
match c as c0 return C c0 with
| inl' x => d x
| inr' y => e y
end.
(* Three useful finite sets *)
Inductive N_0: Set :=.
Definition R_0
(C:N_0 -> Set)
(c: N_0): C c :=
match c as c0 return (C c0) with
end.
Inductive N_1: Set := zero_1:N_1.
Definition R_1
(C:N_1 -> Set)
(c: N_1)
(d_zero: C zero_1): C c :=
match c as c0 return (C c0) with
| zero_1 => d_zero
end.
Inductive N_2: Set := zero_2:N_2 | one_2:N_2.
Definition R_2
(C:N_2 -> Set)
(c: N_2)
(d_zero: C zero_2)
(d_one: C one_2): C c :=
match c as c0 return (C c0) with
| zero_2 => d_zero
| one_2 => d_one
end.
(* Natural numbers *)
Inductive N:Set :=
zero: N | succ : N -> N.
Print N.
Print N_rect.
Definition R
(C:N -> Set)
(d: C zero)
(e: (forall x:N, C x -> C (succ x))):
(forall n:N, C n) :=
fix F (n: N): C n :=
match n as n0 return (C n0) with
| zero => d
| succ n0 => e n0 (F n0)
end.
(* Boolean to truth-value converter *)
Definition Tr (c:N_2) : Set :=
match c as c0 with
| zero_2 => N_0
| one_2 => N_1
end.
(* Identity type *)
Inductive I (A: Set)(x: A) : A -> Set :=
r : I A x x.
Print I_rect.
Theorem J
(A:Set)
(C: (forall x y:A,
forall z: I A x y, Set))
(d: (forall x:A, C x x (r A x)))
(a:A)(b:A)(c:I A a b): C a b c.
induction c.
apply d.
Defined.
(* functions are extensional wrt
identity types *)
Theorem I_I_extensionality (A B: Set)(f: A -> B):
(forall x y:A, I A x y -> I B (f x) (f y)).
Proof.
intros x y P.
induction P.
apply r.
Defined.
(* addition *)
Definition add (m n:N) : N
:= R (fun z=> N) m (fun x y => succ y) n.
(* multiplication *)
Definition mul (m n:N) : N
:= R (fun z=> N) zero (fun x y => add y m) n.
(* Axioms of Peano verified *)
Theorem P1a: (forall x: N, I N (add x zero) x).
intro x.
(* force use of definitional equality
by applying reflexivity *)
apply r.
Defined.
Theorem P1b: (forall x y: N,
I N (add x (succ y)) (succ (add x y))).
intros.
apply r.
Defined.
Theorem P2a: (forall x: N, I N (mul x zero) zero).
intros.
apply r.
Defined.
Theorem P2b: (forall x y: N,
I N (mul x (succ y)) (add (mul x y) x)).
intros.
apply r.
Defined.
Definition pd (n: N): N :=
R (fun _=> N) zero (fun x y=> x) n.
(* alternatively
Definition pd (x: N): N :=
match x as x0 with
| zero => zero
| succ n0 => n0
end.
*)
Theorem P3: (forall x y:N,
I N (succ x) (succ y) -> I N x y).
intros x y p.
apply (I_I_extensionality N N pd (succ x) (succ y)).
apply p.
Defined.
Definition not (A:Set): Set:= (A -> N_0).
Definition isnonzero (n: N): N_2:=
R (fun _ => N_2) zero_2 (fun x y => one_2) n.
Theorem P4 : (forall x:N,
not (I N (succ x) zero)).
intro x.
intro p.
apply (J N (fun x y z =>
Tr (isnonzero x) -> Tr (isnonzero y))
(fun x => (fun t => t)) (succ x) zero)
.
apply p.
simpl.
apply zero_1.
Defined.
Theorem P5 (P:N -> Set):
P zero -> (forall x:N, P x -> P (succ x))
-> (forall x:N, P x).
intros base step n.
apply R.
apply base.
apply step.
Defined.
(* I(A,-,-) is an equivalence relation *)
Lemma Ireflexive (A:Set): (forall x:A, I A x x).
intro x.
apply r.
Defined.
Lemma Isymmetric (A:Set): (forall x y:A, I A x y -> I A y x).
intros x y P.
induction P.
apply r.
Defined.
Lemma Itransitive (A:Set):
(forall x y z:A, I A x y -> I A y z -> I A x z).
intros x y z P Q.
induction P.
assumption.
Defined.
Definition or (A B : Set):= sum' A B.
(* arithmetical expressions *)
Inductive Aexp :Set :=
zer: Aexp
| suc: Aexp -> Aexp
| pls: Aexp -> Aexp -> Aexp.
(* denotation of an expression *)
Definition denotation: Aexp->N:=
fix F (a: Aexp): N :=
match a as a0 with
| zer => zero
| suc a1 => succ (F a1)
| pls a1 a2 => add (F a1) (F a2)
end.
(* predicate for distinguishing
canonical expressions *)
Definition Canonical (x:Aexp):Set :=
or (I Aexp x zer)
(Sigma Aexp (fun y =>
I Aexp x (suc y))).
(* the computation relation is
an inductively defined relation *)
Inductive Comp : Aexp -> Aexp -> Set
:=
refrule: forall a: Aexp,
forall p: Canonical a, Comp a a
| zerrule: forall a b c:Aexp,
forall p: Comp b zer,
forall q: Comp a c,
Comp (pls a b) c
| sucrule: forall a b c:Aexp,
forall p: Comp b (suc c),
Comp (pls a b) (suc (pls a c)).
(* Computations only give canonical
expressions as results *)
Theorem Only_canonical_results:
(forall x y: Aexp, Comp x y -> Canonical y).
admit.
Defined.
(* Here is where help is needed *)
(* Correctness: the computation relation
preserves denotation of expressions *)
Theorem correct_wrt_semantics:
(forall x y: Aexp, Comp x y ->
I N (denotation x) (denotation y)).
admit.
(* Here is where help is need*)
Defined.
(* every input leads to some result *)
Theorem Comp_is_total: (forall x:Aexp,
(Sigma Aexp (fun y =>
prod (Comp x y) (Canonical y)))).
admit.
(* Proof required *)
Defined.
The first two theorems can be proved almost blindly. They follow by induction on the definition of Comp. The third one requires some thinking and some auxiliary theorems though. But you should be following a tutorial if you want to learn Coq.
About the tactics I used:
induction 1 does induction on the first unnamed hypothesis.
info_eauto tries to finish a goal by blindly applying theorems.
Hint Constructors adds the constructors of an inductive definition to the database of theorems info_eauto can use.
unfold, simpl, and rewrite should be self-explanatory.
.
Hint Constructors sum' prod Sigma I Comp.
Theorem Only_canonical_results:
(forall x y: Aexp, Comp x y -> Canonical y).
unfold Canonical, or.
induction 1.
info_eauto.
info_eauto.
info_eauto.
Defined.
Theorem correct_wrt_semantics:
(forall x y: Aexp, Comp x y ->
I N (denotation x) (denotation y)).
induction 1.
info_eauto.
simpl. rewrite IHComp1. rewrite IHComp2. simpl. info_eauto.
simpl. rewrite IHComp. simpl. info_eauto.
Defined.
Theorem Comp_is_total: (forall x:Aexp,
(Sigma Aexp (fun y =>
prod (Comp x y) (Canonical y)))).
unfold Canonical, or.
induction x.
eapply Spair. eapply pair.
eapply refrule. unfold Canonical, or. info_eauto.
info_eauto.
Admitted.

Resources