Rewrite tactic fails to find term occurrence within pattern matching - coq

In Coq, I'm having problems with applying the rewrite tactic in the following situation:
Section Test.
Hypothesis s t : nat -> nat.
Hypothesis s_ext_eq_t : forall (x : nat), s x = t x.
Definition dummy_s : nat -> nat :=
fun n => match n with
| O => 42
| S np => s np
end.
Definition dummy_t : nat -> nat :=
fun n => match n with
| O => 42
| S np => t np
end.
Goal forall (n : nat), dummy_s n = dummy_t n.
Proof.
intro n. unfold dummy_s. unfold dummy_t.
At that stage, the local context and current goal look as follows:
1 subgoals, subgoal 1 (ID 6)
s : nat -> nat
t : nat -> nat
s_ext_eq_t : forall x : nat, s x = t x
n : nat
============================
match n with
| 0 => 42
| S np => s np
end = match n with
| 0 => 42
| S np => t np
end
Now it should be possible to apply the rewrite tactic to replace the occurence of s np in the goal by t np, thereby making it possible to solve the goal using reflexivity. However,
rewrite s_ext_eq_t.
gives
Toplevel input, characters 0-18:
Error: Found no subterm matching "s ?190" in the current goal.
What am I doing wrong? One can get into a situation where rewrite is applicable via
destruct n.
(* n = 0 *)
reflexivity.
(* n > 0 *)
rewrite s_ext_eq_t.
reflexivity.
Qed.
but in the actual situation I am facing, several such destructs would be necessary, and I wonder whether rewrite or a variant of it is able to do this automatically.
Addendum The above situation naturally occurs when proving that a function defined via well-founded recursion has the desired fixed point property:
Suppose A: Type and that R: A -> A -> Prop is a well-founded relation, i.e. we have Rwf: well_founded R. Then, given a type family P: A -> Type we may construct a section
Fix : forall (x : A), P a
through recursion over R, with the recursion step given as a function
F : forall x:A, (forall y:A, R y x -> P y) -> P x
See https://coq.inria.fr/library/Coq.Init.Wf.html However, to show that Fix indeed has the fixed point property
forall (x : A), Fix x = F (fun (y:A) _ => Fix y)`
we need to provide a witness
F_ext : forall (x:A) (f g:forall y:A, R y x -> P y),
(forall (y:A) (p:R y x), f y p = g y p) -> F f = F g.
i.e. we have to show that F does not use anything else from the given f: forall y:A, R y x -> P y but its values. Of course, in any concrete situation, this should be trivial to verify, but when one tries to prove it, one runs into a situation a minimal example of which I have presented above: One is facing a huge equality of two copies of the code for the recursion step, one time with f and another time with g. Your assumption tells that f and g are extensionally equal, so one should be able to rewrite them. However, in the code for the recursion step, there might be a large number of pattern matchings and new variables that doesn't make sense in the local context, hence it would be (unnecessarily?) quite tedious to destruct dozens of times before being allowed to apply rewrite.

As mentioned in a comment above, it is not possible to perform the rewrite directly on the branch of the match statement, because np is not in scope in the top-level environment. As far as Coq's theory is concerned, a proof of your statement will have to destruct n at some point.
Although I am not aware of any tactics for automating this kind of problem, it is not too hard to come up with some custom ltac code for solving your problem without too much pain:
Ltac solve_eq :=
try reflexivity;
match goal with
| |- match ?x with _ => _ end
= match ?x with _ => _ end =>
destruct x; auto
end.
Goal forall (n : nat), dummy_s n = dummy_t n.
Proof.
intro n. unfold dummy_s. unfold dummy_t.
solve_eq.
Qed.
If your extensional equality results are hypotheses that appear in your context, then solve_eq should be able to solve many goals of this shape; if not, you might have to add extra lemmas to your hint database.

Related

Make two arbitrary variables the same in Coq

I have the following coq code:
Theorem filter_exercise : forall (X : Type) (l lf : list X) (test : X -> bool)
(x : X),
filter test l = x :: lf ->
test x = true.
Proof.
intros X l lf test x eq.
induction l as [|l' l].
- inversion eq.
- inversion eq as [H].
Which gives me:
X : Type
l' : X
l, lf : list X
test : X -> bool
x : X
eq : filter test (l' :: l) = x :: lf
testEq : test x = false
IHl : filter test l = x :: lf -> false = true
============================
filter test l = (if test l' then l' :: filter test l else filter test l)
Here, if I could just say that because test x = false and both x and l' are universally quantified variables of type X, then I'd be done with the proof.
However, that's a semantic argument, and I'm not sure how to do that in Coq. Am I going down the wrong path?
EDIT
For posterity, this is the solution I ultimately obtained:
Theorem filter_exercise : forall (X : Type) (l lf : list X) (test : X -> bool)
(x : X),
filter test l = x :: lf ->
test x = true.
Proof.
intros X l lf test x eq.
induction l as [|l' l].
- inversion eq.
- simpl in eq. destruct (test l') eqn:testl'.
+ inversion eq. rewrite <- H0. apply testl'.
+ apply IHl. apply eq.
Qed.
I am not sure what you mean by "semantic argument", but this proof strategy is not correct, neither on paper nor in Coq. Consider, for instance, the following statement:
Lemma faulty : forall n m : nat, even n -> even m.
Proof. Admitted.
By your logic, if n is even, then m should also be even, since both are universally quantified variables of type nat. However, precisely because they are universally quantified, they can instantiated to different values of nat, thus yielding obviously contradictory statements. For instance, if we instantiate faulty with 2 and 1, we should be able to conclude that 1 is even, which is not true.
Your argument that test x = false -> test l' = false is not true, as both variables x and l are universally quantified, and thus can have any value. You could just have a specific relationship between both variables in your hypothesis, but here it is not the case, except the relationship filter test (l' :: l) = x :: lf, that tells you that x could be an element of l which has not been filtered by test (but it also might be l').
You should not use inversion here, as your problem is really simple. You idea to perform an induction is fine however :
Try first to simplify several hypothesis.
Then see if there are different cases to deal with, and use destruct when needed (on the value of test l' in this problem)
You should be able to solve the problem then (the most complicated tactic you might have to use is injection)

Pigeonhole proof without decidable equality or excluded middle

In Software Foundations IndProp.v one is asked to prove the pigeonhole principle, and one may use excluded middle, but it is mentioned that it is not strictly necessary. I've been trying to prove it without EM, but my brain seems to be wired classically.
Q: How would one prove the theorem without using excluded middle? How should one generally approach proofs for types without decidable equality, where one can't easily reason by cases?
I'd be very happy for a complete proof to look at, but please avoid posting it "in the clear", so as to not spoil the exercise in the Software Foundations course.
The definition uses two inductive predicates, In and repeats.
Require Import Lists.List.
Import ListNotations.
Section Pigeon.
Variable (X:Type).
Implicit Type (x:X).
Fixpoint In x l : Prop := (*** In ***)
match l with
| nil => False
| (x'::l') => x' = x \/ In x l'
end.
Hypothesis in_split : forall x l, In x l -> exists l1 l2, l = l1 ++ x :: l2.
Hypothesis in_app: forall x l1 l2, In x (l1++l2) <-> In x l1 \/ In x l2.
Inductive repeats : list X -> Prop := (*** repeats ***)
repeats_hd l x : In x l -> repeats (x::l)
| repeats_tl l x : repeats l -> repeats (x::l).
Theorem pigeonhole_principle_NO_EM: (*** pigeonhole ***)
forall l1 l2,
length l2 < length l1 -> (* There are more pigeons than nests *)
(forall x, In x l1 -> In x l2) -> (* All pigeons are in some nest *)
repeats l1. (* Thus, some pigeons share nest *)
Proof.
(* ??? *)
I'll describe the thought process that led me to a solution, in case it helps. We may apply induction and it is straightforward to reduce to the case l1 = a::l1', l2 = a::l2'. Now l1' is a subset of a::l2'. My EM-trained intuition is that one of the following holds:
a is in l1'.
a is not in l1'.
In the latter case, each element of l1' is in a::l2' but differs from a, and therefore must be in l2'. Thus l1' is a subset of l2', and we can apply the inductive hypothesis.
Unfortunately if In is not decidable, the above can't be directly formalized. In particular if equality is not decidable for the given type, it's difficult to prove elements are unequal, and therefore difficult to prove a negative statement like ~(In a l1'). However, we wanted to use that negative statement to prove a positive one, namely
forall x, In x l1' -> In x l2'
By analogy, suppose we wanted to prove
P \/ Q
Q -> R
------
P \/ R
The above intuitive argument is like starting from P \/ ~P, and using ~P -> Q -> R in the second case. We can use a direct proof to avoid EM.
Quantifying over the list l1' makes this a bit more complicated, but still we can construct a direct proof using the following lemma, which can be proven by induction.
Lemma split_or {X} (l : list X) (P Q : X -> Prop) :
(forall x, In x l -> (P x \/ Q x)) ->
(exists x, In x l /\ P x) \/ (forall x, In x l -> Q x).
Finally note that the intuitive pigeonhole principle could also be formalized as the following way, which cannot be proven when the type has undecidable equality (note that it has a negative statement in the conclusion):
Definition pigeon2 {X} : Prop := forall (l1 l2 : list X),
length l2 < length l1 ->
(exists x, In x l1 /\ ~(In x l2)) \/ repeats l1.
A possible constructive proof goes like this:
We prove pigeonhole_principle_NO_EM by induction on l1. Only the non-empty case is possible because of the length constraint. So, assume l1 = x :: l1'. Now, check whether there is some element of l1' which is mapped by f : (forall x, In x l1 -> In x l2) to the same membership proof as x. If there is such an x' element, then it follows that x = x', therefore l1 repeats. If there is no such element, then we can get l2' by removing the element that x is mapped to from l2, and apply the induction hypothesis to l2' and the appropriate f' : forall x, In x l1' -> In x l2' function.
That's it, but I note that actually formalizing this proof is not easy with the definitions given, because we need to prove heterogeneous or dependent equalities, since we have to compare membership proofs for possibly different elements.
As to the question of getting the hang of constructive proofs in general, an important skill or habit is always examining what kind of data we have, not just what kind of logical facts we know. In this case, membership proofs are actually indices pointing into lists, bundled together with proofs that the pointed-to elements equal certain values. If membership proofs didn't tell where exactly elements are located then this proof would not be possible constructively.

Abstracting over the term ... leads to a term ... which is ill-typed

Here is what I am trying to prove:
A : Type
i : nat
index_f : nat → nat
n : nat
ip : n < i
partial_index_f : nat → option nat
L : partial_index_f (index_f n) ≡ Some n
V : ∀ i0 : nat, i0 < i → option A
l : ∀ z : nat, partial_index_f (index_f n) ≡ Some z → z < i
============================
V n ip
≡ match
partial_index_f (index_f n) as fn
return (partial_index_f (index_f n) ≡ fn → option A)
with
| Some z => λ p : partial_index_f (index_f n) ≡ Some z, V z (l z p)
| None => λ _ : partial_index_f (index_f n) ≡ None, None
end eq_refl
The obvious next step is either rewrite L or destruct (partial_index_f (index_f n). Trying to applying rewrite gives me an error:
Error: Abstracting over the term "partial_index_f (index_f n)"
leads to a term
"λ o : option nat,
V n ip
≡ match o as fn return (o ≡ fn → option A) with
| Some z => λ p : o ≡ Some z, V z (l z p)
| None => λ _ : o ≡ None, None
end eq_refl" which is ill-typed.
I do not understand what is causing this problem. I also would like to understand how I can deal with it in general.
I was able to prove it using the following steps, but I am not sure this is the best way:
destruct (partial_index_f (index_f n)).
inversion L.
generalize (l n0 eq_refl).
intros. subst n0.
replace l0 with ip by apply proof_irrelevance.
reflexivity.
congruence.
In Coq's theory, when you perform a rewrite with an equation, you have to generalize over the side of the equation that you want to replace. In your case, you want to replace partial_index_f (index_f n), so Coq tries to generalize that, as you can tell from the error message you got.
Now, if your goal contains something whose type mentions the thing that you want to replace, you might run into trouble, because this generalization might make the goal become ill-typed. (Notice that that type does not exactly occur in the goal, hence Coq does not try to deal with it like it does when something occurs in the goal.) Going back to your case, your l function has type ∀ z : nat, partial_index_f (index_f n) ≡ Some z → z < i, which mentions partial_index_f (index_f n), the term you want to replace. In the first branch of your match, you apply this function to the o = Some z hypothesis that you abstracted over. On the original goal, o was the thing you wanted to replace, but when Coq tries to generalize, the two do not match anymore, hence the error message.
I can't try to fix the problem on my own, but you can solve issues like this usually by generalizing over the term in your context that mentions the term you are replacing, because then its type will show in the goal, associated to a universally quantified variable. This might not help if your term is defined globally and you need it to have a certain shape after the rewrite in order to be able to perform additional reasoning steps, in which case you will probably have to generalize over the lemmas that you need as well.

Incorrect elimination of X in the inductive type "or":

I am trying to define a relatively simple function on Coq:
(* Preliminaries *)
Require Import Vector.
Definition Vnth {A:Type} {n} (v : Vector.t A n) : forall i, i < n -> A. admit. Defined.
(* Problematic definition below *)
Definition VnthIndexMapped {A:Type}
{i o:nat}
(x: Vector.t (option A) i)
(f': nat -> option nat)
(f'_spec: forall x, x<o ->
(forall z,(((f' x) = Some z) -> z < i)) \/
(f' x = None))
(n:nat) (np: n<o)
: option A
:=
match (f' n) as fn, (f'_spec n np) return f' n = fn -> option A with
| None, _ => fun _ => None
| Some z, or_introl zc1 => fun p => Vnth x z (zc1 z p)
| Some z, or_intror _ => fun _ => None (* impossible case *)
end.
And getting the following error:
Error:
Incorrect elimination of "f'_spec n np" in the inductive type "or":
the return type has sort "Type" while it should be "Prop".
Elimination of an inductive object of sort Prop
is not allowed on a predicate in sort Type
because proofs can be eliminated only to build proofs.
I think I understand the reason for this limitation, but I am having difficulty coming up with a workaround. How something like this could be implemented? Basically I have a function f' for which I have a separate proof that values less than 'o' it either returns None or a (Some z) where z is less than i and I am trying to use it in my definition.
There are two approaches to a problem like this: the easy way and the hard way.
The easy way is to think whether you're doing anything more complicated than you have to. In this case, if you look carefully, you will see that your f'_spec is equivalent to the following statement, which avoids \/:
Lemma f'_spec_equiv i o (f': nat -> option nat) :
(forall x, x<o ->
(forall z,(((f' x) = Some z) -> z < i)) \/
(f' x = None))
<-> (forall x, x<o -> forall z,(((f' x) = Some z) -> z < i)).
Proof.
split.
- intros f'_spec x Hx z Hf.
destruct (f'_spec _ Hx); eauto; congruence.
- intros f'_spec x Hx.
left. eauto.
Qed.
Thus, you could have rephrased the type of f'_spec in VnthIndexedMapped and used the proof directly.
Of course, sometimes there's no way of making things simpler. Then you need to follow the hard way, and try to understand the nitty-gritty details of Coq to make it accept what you want.
As Vinz pointed out, you usually (there are exceptions) can't eliminate the proof of proposition to construct something computational. However, you can eliminate a proof to construct another proof, and maybe that proof gives you what need. For instance, you can write this:
Definition VnthIndexMapped {A:Type}
{i o:nat}
(x: Vector.t (option A) i)
(f': nat -> option nat)
(f'_spec: forall x, x<o ->
(forall z,(((f' x) = Some z) -> z < i)) \/
(f' x = None))
(n:nat) (np: n<o)
: option A
:=
match (f' n) as fn return f' n = fn -> option A with
| None => fun _ => None
| Some z => fun p =>
let p' := proj1 (f'_spec_equiv i o f') f'_spec n np z p in
Vnth x z p'
end eq_refl.
This definition uses the proof that both formulations of f'_spec are equivalent, but the same idea would apply if they weren't, and you had some lemma allowing you to go from one to the other.
I personally don't like this style very much, as it is hard to use and lends itself to programs that are complicated to read. But it can have its uses...
The issue is that you want to build a term by inspecting the content of f'_spec. This disjunction lives in Prop, so it can only build other Prop. You want to build more, something in Type. Therefore you need a version of disjunction that lives at least in Set (more generally in Type). I advise you replace your Foo \/ Bar statement with the usage of sumbool, which uses the notation {Foo}+{Bar}.

Coq dependent types

I am new to Coq and need some help with some of trivial examples to get me started. In particular I am interested in defining some operations of vectors (fixed size lists) using dependent types. I started with Vector package and trying to implement some additional functions. For example I am having difficulty implementing trivial 'take' and 'drop' functions, which take or drop first 'p' elements from the list.
Require Import Vector.
Fixpoint take {A} {n} (p:nat) (a: t A n) : p<=n -> t A p :=
match a return ( p<=n -> t A p) with
| cons A v (S m) => cons (hd v) (take m (tl v)) m
| nil => fun pf => a
end.
The error (in case of nil) is:
The term "a" has type "t A n" while it is expected to have type "t A p".
Could somebody help me with some starting points? Thanks!
I don't understand your approach. You're always returning a non-empty vector when the argument is a non-empty vector, but take must return nil when p=0 regardless of the vector.
Here's one approach to building take. Rather than using the hypothesis p <= n, I express the length of the argument n as a sum of the number p of elements to take and the number of trailing elements m, which is possible iff p <= n. This allows for an easier recursive definition, because (S p') + m is structurally equal to S (p' + m). Note that the discrimination is on the number of elements to take: return nil if taking 0, return cons head new_tail otherwise.
This version of the take function has the desired computational behavior, so all that's left is to define one with the desired proof content. I use the Program feature to do this conveniently: fill in the computational content (trivial, I just need to say that I want to use m = n - p), then complete the proof obligations (which are simple arithmetic).
Require Import Arith.
Require Import Vector.
Fixpoint take_plus {A} {m} (p:nat) : t A (p+m) -> t A p :=
match p return t A (p+m) -> t A p with
| 0 => fun a => nil _
| S p' => fun a => cons A (hd a) _ (take_plus p' (tl a))
end.
Program Definition take A n p (a : t A n) (H : p <= n) : t A p :=
take_plus (m := n - p) p a.
Solve Obligations using auto with arith.
For your newdrop : forall A n p, t A n -> p <= n -> t A (n-p), the following approach works. You need to help Coq by telling it what p and n become in the recursive call.
Program Fixpoint newdrop {A} {n} p : t A n -> p <= n -> t A (n-p) :=
match p return t A n -> p <= n -> t A (n-p) with
| 0 => fun a H => a
| S p' => fun a H => newdrop p' (tl a) (_ : p' <= n - 1)
end.
Next Obligation.
omega.
Qed.
Next Obligation.
omega.
Qed.
Next Obligation.
omega.
Qed.
Next Obligation.
omega.
Qed.
I don't know why Solve Obligations using omega. doesn't work but solving each obligation individually works.