Prove equality on list constructed with a map - coq

I have two lists, one constructed directly by recursion and the other constructed using a map operation. I'm trying to show they are equal, and surprisingly I got stuck.
Require Import Coq.Lists.List.
Import ListNotations.
Fixpoint ls_zeroes n :=
match n with
| 0 => nil
| S n' => 0 :: ls_zeroes n'
end.
Fixpoint ls_ones n := map S (ls_zeroes n).
Fixpoint ls_ones' n :=
match n with
| 0 => nil
| S n' => 1 :: ls_ones' n'
end.
Goal forall n, ls_ones n = ls_ones' n.
Proof.
intros.
induction n.
- reflexivity.
- simpl. f_equal. (* ??? *)
Abort.
This is what the context looks like:
1 subgoal
n : nat
IHn : ls_ones n = ls_ones' n
______________________________________(1/1)
map S (ls_zeroes n) = ls_ones' n
I thought fold ls_ones would map S (ls_zeroes n) into ls_ones n since that's literally the definition of ls_ones but it does nothing. If I try to unfold ls_ones in IHn I get a nasty recursive expression instead of the verbatim definition of ls_ones.
What is the cleanest way to complete this proof?

Notice that when you define ls_one and unfold the definition you gets :
(fix ls_ones (n0 : nat) : list nat := map S (ls_zeroes n0)) n = ls_ones' n
The problem is that ls_one isn't a fixpoint. Indeed, it's doesn't make a recursion. Once coq automatically defines the point {struct n0} (in that case the n argument), your proof gets stuck because n is never destructed in P k -> P (k + 1), 'cause k is not destructed.
Using :
Definition ls_ones n := map S (ls_zeroes n).
The proof becomes trivial :
Goal forall n, ls_ones n = ls_ones' n.
Proof.
intros.
induction n.
trivial.
unfold ls_ones in *.
simpl.
rewrite IHn.
trivial.
Qed.

I thought fold ls_ones would map S (ls_zeroes n) into ls_ones n since that's literally the definition of ls_ones
Is it? You said Fixpoint ls_ones, not Definition. Just like any Fixpoint, this means that the given definition of ls_ones is transformed into a fix. There's no recursive structure in the definition given, so this is pointless, but you said to do it, so Coq does it. Issue Print ls_ones. to see the actual definition. The true solution is to make ls_ones a Definition.
If you don't fix that, Coq will only reduce a Fixpoint if the recursive argument(s) start with constructors. Then, in order to complete this proof, you need to destruct n to show those constructors:
Goal forall n, ls_ones n = ls_ones' n.
Proof.
intros.
induction n.
- reflexivity.
- simpl. f_equal. destruct n; assumption.
Qed.

Unfortunately, due to the value being fixed in your definitions you must use induction to do the proof:
From mathcomp Require Import all_ssreflect.
Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.
Fixpoint seq0 n :=
match n with
| 0 => nil
| S n' => 0 :: seq0 n'
end.
Fixpoint seq1 n :=
match n with
| 0 => nil
| S n' => 1 :: seq1 n'
end.
Lemma eq_F n : seq1 n = [seq n.+1 | n <- seq0 n].
Proof. by elim: n => //= n ->. Qed.
There is not a lot to proof tho. I'd recommend tho using the more general nseq count elem function instead of definition your own duplicate structures, then the proof follows pretty quickly from the general lemma about map:
Lemma eq_G n : nseq n 1 = [seq n.+1 | n <- nseq n 0].
Proof. by rewrite map_nseq. Qed.

Related

Why does Ltac not match the clause?

In the following Coq proof:
Ltac easy_ltac t := match goal with
| [Z: #eq nat t ?Y |- _ ] => pose ?Y as N
end.
Lemma easy: forall (n: nat), (n >= O)%nat.
Proof.
intros n. destruct n eqn: M. easy_ltac n.
I get the error message: "No matching clauses for match.", while I have the hypothesis
M : #eq nat n O
I do not understand why.
The culprit is the extraneous ? before the 2nd occurrence of Y.
Also just to recall, you might want to use the fresh tactic rather than hard-coded N as an hypothesis name.
So, it should work if you just write:
Ltac easy_ltac t := match goal with
| [Z: #eq nat t ?Y |- _ ] => let N := fresh "N" in pose Y as N
end.
Lemma easy: forall (n: nat), (n >= O)%nat.
Proof.
intros n. destruct n eqn: M. easy_ltac n. easy_ltac n.

How to unfold a Coq fixpoint by one iteration

I have the following in my proof environment:
1 subgoal
a, b : nat
H : (fix loop (m : nat) : nat :=
match (m - a) with
| 0 => m
| S m' => loop m'
end) b = 0
G : (b - a) = 0
Clearly, H is equivalent to
match (b - a) with
| 0 => b
| S m' => loop m'
end = 0
Which would then allow me to rewrite using G.
But since it is trapped in there, represented as (m - a), I cannot rewrite using G.
How do I unfold a fixpoint out by one iteration?
Edit: The following code will set up the proof environment. Just ignore the admit statements. Your goal is not to prove the statement (which is trivial) but to "unfold" the fixpoint.
From mathcomp Require Import all.
Goal forall a b : nat,
modn b a = 0 -> True.
Proof.
intros a b H.
unfold modn in H.
destruct a.
+ admit.
+ simpl in H.
assert ((b - a) = 0) as G.
- admit.
- unfold modn_rec in H.
To unfold a fixpoint you need to destruct its decreasing argument.
destruct b; simpl in H.
If you want to keep a single case, you'll have to prove the equality you mention in a separate lemma or assertion.
assert (Hfix : (fix loop m := match ... end) b = match ... end)

How to prove that terms of a first-order language are well-founded?

Currently, I've started working on proving theorems about first-order logic in Coq(VerifiedMathFoundations). I've proved deduction theorem, but then I got stuck with lemma 1 for theorem of correctness. So I've formulated one elegant piece of the lemma compactly and I invite the community to look at it. That is an incomplete the proof of well-foundness of the terms. How to get rid of the pair of "admit"s properly?
(* PUBLIC DOMAIN *)
Require Export Coq.Vectors.Vector.
Require Export Coq.Lists.List.
Require Import Bool.Bool.
Require Import Logic.FunctionalExtensionality.
Require Import Coq.Program.Wf.
Definition SetVars := nat.
Definition FuncSymb := nat.
Definition PredSymb := nat.
Record FSV := {
fs : FuncSymb;
fsv : nat;
}.
Record PSV := MPSV{
ps : PredSymb;
psv : nat;
}.
Inductive Terms : Type :=
| FVC :> SetVars -> Terms
| FSC (f:FSV) : (Vector.t Terms (fsv f)) -> Terms.
Definition rela : forall (x y:Terms), Prop.
Proof.
fix rela 2.
intros x y.
destruct y as [s|f t].
+ exact False.
+ refine (or _ _).
exact (Vector.In x t).
simple refine (#Vector.fold_left Terms Prop _ False (fsv f) t).
intros Q e.
exact (or Q (rela x e)).
Defined.
Definition snglV {A} (a:A) := Vector.cons A a 0 (Vector.nil A).
Definition wfr : #well_founded Terms rela.
Proof.
clear.
unfold well_founded.
assert (H : forall (n:Terms) (a:Terms), (rela a n) -> Acc rela a).
{ fix iHn 1.
destruct n.
+ simpl. intros a b; destruct b.
+ simpl. intros a Q. destruct Q as [L|R].
* admit. (* smth like apply Acc_intro. intros m Hm. apply (iHn a). exact Hm. *)
* admit. (* like in /Arith/Wf_nat.v *)
}
intros a.
simple refine (H _ _ _).
exact (FSC (Build_FSV 0 1) (snglV a)).
simpl.
apply or_introl.
constructor.
Defined.
It is also available here: pastebin.
Update: At least transitivity is needed for well-foundness. I also started a proof, but didn't finished.
Fixpoint Tra (a b c:Terms) (Hc : rela c b) (Hb : rela b a) {struct a}: rela c a.
Proof.
destruct a.
+ simpl in * |- *.
exact Hb.
+ simpl in * |- *.
destruct Hb.
- apply or_intror.
revert f t H .
fix RECU 1.
intros f t H.
(* ... *)
Admitted.
You can do it by defining a height function on Terms, and showing that decreasing rela implies decreasing heights:
Require Export Coq.Vectors.Vector.
Require Export Coq.Lists.List.
Require Import Bool.Bool.
Require Import Logic.FunctionalExtensionality.
Require Import Coq.Program.Wf.
Definition SetVars := nat.
Definition FuncSymb := nat.
Definition PredSymb := nat.
Record FSV := {
fs : FuncSymb;
fsv : nat;
}.
Record PSV := MPSV{
ps : PredSymb;
psv : nat;
}.
Unset Elimination Schemes.
Inductive Terms : Type :=
| FVC :> SetVars -> Terms
| FSC (f:FSV) : (Vector.t Terms (fsv f)) -> Terms.
Set Elimination Schemes.
Definition Terms_rect (T : Terms -> Type)
(H_FVC : forall sv, T (FVC sv))
(H_FSC : forall f v, (forall n, T (Vector.nth v n)) -> T (FSC f v)) :=
fix loopt (t : Terms) : T t :=
match t with
| FVC sv => H_FVC sv
| FSC f v =>
let fix loopv s (v : Vector.t Terms s) : forall n, T (Vector.nth v n) :=
match v with
| #Vector.nil _ => Fin.case0 _
| #Vector.cons _ t _ v => fun n => Fin.caseS' n (fun n => T (Vector.nth (Vector.cons _ t _ v) n))
(loopt t)
(loopv _ v)
end in
H_FSC f v (loopv _ v)
end.
Definition Terms_ind := Terms_rect.
Fixpoint height (t : Terms) : nat :=
match t with
| FVC _ => 0
| FSC f v => S (Vector.fold_right (fun t acc => Nat.max acc (height t)) v 0)
end.
Definition rela : forall (x y:Terms), Prop.
Proof.
fix rela 2.
intros x y.
destruct y as [s|f t].
+ exact False.
+ refine (or _ _).
exact (Vector.In x t).
simple refine (#Vector.fold_left Terms Prop _ False (fsv f) t).
intros Q e.
exact (or Q (rela x e)).
Defined.
Require Import Lia.
Definition wfr : #well_founded Terms rela.
Proof.
apply (Wf_nat.well_founded_lt_compat _ height).
intros t1 t2. induction t2 as [sv2|f2 v2 IH]; simpl; try easy.
intros [t_v|t_sub]; apply Lt.le_lt_n_Sm.
{ clear IH. induction t_v; simpl; lia. }
revert v2 IH t_sub; generalize (fsv f2); clear f2.
intros k v2 IH t_sub.
enough (H : exists n, rela t1 (Vector.nth v2 n)).
{ destruct H as [n H]. apply IH in H. clear IH t_sub.
transitivity (height (Vector.nth v2 n)); try lia; clear H.
induction v2 as [|t2 m v2 IHv2].
- inversion n.
- apply (Fin.caseS' n); clear n; simpl; try lia.
intros n. specialize (IHv2 n). lia. }
clear IH.
assert (H : Vector.fold_right (fun t Q => Q \/ rela t1 t) v2 False).
{ revert t_sub; generalize False.
induction v2 as [|t2 n v2]; simpl in *; trivial.
intros P H; specialize (IHv2 _ H); clear H.
induction v2 as [|t2' n v2 IHv2']; simpl in *; tauto. }
clear t_sub.
induction v2 as [|t2 k v2 IH]; simpl in *; try easy.
destruct H as [H|H].
- apply IH in H.
destruct H as [n Hn].
now exists (Fin.FS n).
- now exists Fin.F1.
Qed.
(Note the use of the custom induction principle, which is needed because of the nested inductives.)
This style of development, however, is too complicated. Avoiding certain pitfalls would greatly simplify it:
The Coq standard vector library is too hard to use. The issue here is exacerbated because of the nested inductives. It would probably be better to use plain lists and have a separate well-formedness predicate on terms.
Defining a relation such as rela in proof mode makes it harder to read. Consider, for instance, the following simpler alternative:
Fixpoint rela x y :=
match y with
| FVC _ => False
| FSC f v =>
Vector.In x v \/
Vector.fold_right (fun z P => rela x z \/ P) v False
end.
Folding left has a poor reduction behavior, because it forces us to generalize over the accumulator argument to get the induction to go through. This is why in my proof I had to switch to a fold_right.

How to prove a theorem on natural numbers using Coq list

I'm new in Coq. To do practice on list and list of pairs, I used Coq list library to prove a simple theorem of natural numbers. I try to prove the simple property of natural numbers:
forall n, multiplier, a0....an, d1...dn:
((a0*multiplier)=d1)+((a1*multiplier)=d2)+((a2*multiplier)=d3)+...+((an*multiplier)=dn) = result
-> (a0+a1+a2+...+an) * multiplier = d1+d2+...+dn = result
((3*2)=6)+((5*2)=10)+((9*2)=18) = 34 -> (3+5+9)*2 = 6+10+18 = 34 can be an example of this property(i.e. n=3 and multiplier = 2).
I use list of pairs (storing a's in one list and d's in another list) to code this property in Coq as:
Require Import List.
Fixpoint addnumbers (L : list nat) : nat :=
match L with
| nil => 0
| H::tail => H + addnumbers tail
end.
Theorem resultAreEqual : forall (natListofpair :list (nat * nat))
(multiplier : nat) (result : nat),
Forall (fun '(a,d) => a * multiplier = d ) natListofpair ->
addnumbers(List.map (#fst nat nat) natListofpair) * multiplier = result ->
addnumbers (List.map (#snd nat nat) natListofpair) = result.
Proof.
intros.
destruct natListofpair.
subst. simpl. reflexivity.
rewrite <- H0.
inversion H.
destruct p. simpl.
But I don't know how I should continue this prove. I'm stuck in this proving for one week. I'd be thankful for your help.
One reason you are having difficulty is that you have stated your lemma in an indirect way. When proving something in Coq, it is very important that you state it as simple as possible, as this often leads to easier proofs. In this case, the statement can become much simpler by using higher-order functions on lists.
Require Import Coq.Arith.PeanoNat.
Require Import Coq.Lists.List.
Definition sum (l : list nat) := fold_right Nat.add 0 l.
Lemma my_lemma l m : sum (map (Nat.mul m) l) = m * sum l.
The sum function is the equivalent of your addnumbers. The lemma says "the result of multiplying all numbers in l by m and adding them is the same as the result of adding them up first and multiplying by m later".
To prove this result, we need a crucial ingredient that your proof was missing: induction. This is often needed in Coq when we want to reason about objects of unbounded size, such as lists. Here is one possible proof.
Proof.
unfold sum.
induction l as [|x l IH]; simpl.
- (* Nil case *)
now rewrite Nat.mul_0_r.
- (* Cons case *)
now rewrite IH, Nat.mul_add_distr_l.
Qed.

How to do induction on the length of a list in Coq?

When reasoning on paper, I often use arguments by induction on the length of some list. I want to formalized these arguments in Coq, but there doesn't seem to be any built in way to do induction on the length of a list.
How should I perform such an induction?
More concretely, I am trying to prove this theorem. On paper, I proved it by induction on the length of w. My goal is to formalize this proof in Coq.
There are many general patterns of induction like this one that can be covered
by the existing library on well founded induction. In this case, you can prove
any property P by induction on length of lists by using well_founded_induction, wf_inverse_image, and PeanoNat.Nat.lt_wf_0, as in the following comand:
induction l using (well_founded_induction
(wf_inverse_image _ nat _ (#length _)
PeanoNat.Nat.lt_wf_0)).
if you are working with lists of type T and proving a goal P l, this generates an
hypothesis of the form
H : forall y : list T, length y < length l -> P y
This will apply to any other datatype (like trees for instance) as long as you can map that other datatype to nat using any size function from that datatype to nat instead of length.
Note that you need to add Require Import Wellfounded. at the head of your development for this to work.
Here is how to prove a general list-length induction principle.
Require Import List Omega.
Section list_length_ind.
Variable A : Type.
Variable P : list A -> Prop.
Hypothesis H : forall xs, (forall l, length l < length xs -> P l) -> P xs.
Theorem list_length_ind : forall xs, P xs.
Proof.
assert (forall xs l : list A, length l <= length xs -> P l) as H_ind.
{ induction xs; intros l Hlen; apply H; intros l0 H0.
- inversion Hlen. omega.
- apply IHxs. simpl in Hlen. omega.
}
intros xs.
apply H_ind with (xs := xs).
omega.
Qed.
End list_length_ind.
You can use it like this
Theorem foo : forall l : list nat, ...
Proof.
induction l using list_length_ind.
...
That said, your concrete example example does not necessarily need induction on the length. You just need a sufficiently general induction hypothesis.
Import ListNotations.
(* ... some definitions elided here ... *)
Definition flip_state (s : state) :=
match s with
| A => B
| B => A
end.
Definition delta (s : state) (n : input) : state :=
match n with
| zero => s
| one => flip_state s
end.
(* ...some more definitions elided here ...*)
Theorem automata221: forall (w : list input),
extend_delta A w = B <-> Nat.odd (one_num w) = true.
Proof.
assert (forall w s, extend_delta s w = if Nat.odd (one_num w) then flip_state s else s).
{ induction w as [|i w]; intros s; simpl.
- reflexivity.
- rewrite IHw.
destruct i; simpl.
+ reflexivity.
+ rewrite <- Nat.negb_even, Nat.odd_succ.
destruct (Nat.even (one_num w)), s; reflexivity.
}
intros w.
rewrite H; simpl.
destruct (Nat.odd (one_num w)); intuition congruence.
Qed.
In case like this, it is often faster to generalize your lemma directly:
From mathcomp Require Import all_ssreflect.
Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.
Section SO.
Variable T : Type.
Implicit Types (s : seq T) (P : seq T -> Prop).
Lemma test P s : P s.
Proof.
move: {2}(size _) (leqnn (size s)) => ss; elim: ss s => [|ss ihss] s hs.
Just introduce a fresh nat for the size of the list, and regular induction will work.