Coq: working with inequalities (<>) - coq

I'm trying to understand the logic about working with inequalities in Coq.
When <> is present in the goal, doing intros contra. changes the goal to False and moves the goal to an hypothesis but with <> switched to =. I think I understand how it is sound. If I have as goal a <> b, then a = b as hypothesis would generate a contradiction.
However, I can't do the opposite in Coq. If I have as goal a = b, I can't intros contra. in order to have False as goal and a <> b as hypothesis. Would this intros be logically sound? Is it not supported just because it is never needed to complete a proof?
When <> is in an hypothesis H, doing destruct H. will remove the hypothesis (I can't do destruct (H) eqn:H.) and it will switch any goal to the same that H but with <> switched for =. I don't understand the logic here. If I have an hypothesis which is an inequality, I don't see how not having it is the same as having the equality as goal.
How an inequality is inductive to be used by destruct?
If I have a contradictory hypothesis G 0 <> 0, in order to complete the proof and tell it is a contradiction, I need to do destruct G. (* now the goal is 0 = 0 *). reflexivity. Why is it not possible to just do something like inversion G., as you would do with an hypothesis S n = 0?.
So I have, in fact, 4 related questions marked in bold.

Would this intros [on a goal a = b] be logically sound?
If I understand your question, you want to know if it would be possible to
have a goal a = b, call intros contra, and transform that into the goal H : a <> b |- False. This would be sound, but not derivable in Coq's basic constructive logic for a and b of arbitrary type: it asserts that the proposition a = b supports double-negation elimination (~ (~ a = b) -> a = b). Coq does not support this because it would mean working in a different logical formalism.
How an inequality is inductive to be used by destruct?
As yeputons remarked, a <> b is defined as a = b -> False, and falsity is inductively defined as the proposition with no constructors; thus, destructing something of type False simply completes the proof. Furthermore, calling destruct on something of type A -> B has roughly the effect of generating a goal of type A, feeding that proof into the implication to obtain a proof of B, and then calling destruct on that proof of B. In your case, this means doing exactly what you described.
Why is it not possible to just do something like inversion G., as you would do with an hypothesis S n = 0?
My guess is that inversion is not as lenient as destruct, and is not extended to work on implications as I explained above.

Related

How does one access the dependent type unification algorithm from Coq's internals -- especially the one from apply and the substitution solution?

TLDR: I want to be able to compare two terms -- one with a hole and the other without the hole -- and extract the actual lambda term that complete the term. Either in Coq or in OCaml or a Coq plugin or in anyway really.
For example, as a toy example say I have the theorem:
Theorem add_easy_0'':
forall n:nat,
0 + n = n.
Proof.
The (lambda term) proof for this is:
fun n : nat => eq_refl : 0 + n = n
if you had a partial proof script say:
Theorem add_easy_0'':
forall n:nat,
0 + n = n.
Proof.
Show Proof.
intros.
Show Proof.
Inspected the proof you would get as your partial lambda proof as:
(fun n : nat => ?Goal)
but in fact you can close the proof and therefore implicitly complete the term with the ddt unification algorithm using apply:
Theorem add_easy_0'':
forall n:nat,
0 + n = n.
Proof.
Show Proof.
intros.
Show Proof.
apply (fun n : nat => eq_refl : 0 + n = n).
Show Proof.
Qed.
This closes the proof but goes not give you the solution for ?Goal -- though obviously Coq must have solved the CIC/ddt/Coq unification problem implicitly and closes the goals. I want to get the substitution solution from apply.
How does one do this from Coq's internals? Ideally while remaining in Coq but OCaml internals or coq plugin solutions or in fact any solution I am happy with.
Appendix 1: how did I realize apply must use some sort of "coq unification"
I knew that apply must be doing this because in the description of the apply tactic I know apply must be using unification due to it saying this:
This tactic applies to any goal. The argument term is a term well-formed in the local context. The tactic apply tries to match the current goal against the conclusion of the type of term. If it succeeds, then the tactic returns as many subgoals as the number of non-dependent premises of the type of term.
This is very very similar to what I once saw in a lecture for unification in Isabelle:
with some notes on what that means:
- You have/know rule [[A1; … ;An]] => A (*)
- that says: that given A1; …; An facts then you can conclude A
- or in backwards reasoning, if you want to conclude A, then you must give a proof of A1; …;An (or know Ai's are true)
- you want to close the proof of [[B1; …; Bm]] => C (**) (since thats your subgoal)
- so you already have the assumptions B1; …; Bm lying around for you, but you wish to be able to conclude C
- Say you want to transform subgoal (**) using rule (*). Then this is what’s going on:
- first you need to see if your subgoal (**) is a "special case" of your rule (*). You commence by checking if the conclusion (targets) of the rules are "equivalent". If the conclusions match then instead of showing C you can now show A instead. But for you to have (or show) A, you now need to show A1; … ;An using the substitution that made C and A match. The reason you need to show A1;...;An is because if you show them you get A automatically according to rule (*) -- which by the "match" (unification) shows the original goal you were after. The main catch is that you need to do this by using the substitution that made A and C match. So:
- first see if you can “match” A and C. The conclusions from both side must match. This matching is called unification and returns a substitution sig that makes the terms equal
- sig = Unify(A,C) s.t. sig(A) = sig(C)
- then because we transformed the subgoal (**) using rule (*), we must then proceed to prove the obligations from the rule (*) we used to match to conclusion of the subgoal (**). from the assumptions of the original subgoal in (**) (since those are still true) but using the substitution sig that makes the rules match.
- so the new subgoals if we match the current subgoal (*) with rule (**) is:
- [[sig(B1); … ; sig(Bm) ]] => sigm(A1)
- ...
- [[sig(B1); … ; sig(Bm) ]] => sigm(An)
- Completing/closing the proof above (i.e. proving it) shows/proves:
- [[sig(B1); …;sig(Bm) ]] => sig(C)
- Command: apply (rule <(*)>) where (*) stands for the rule names
Appendix2: why not exact?
Note that initially I thought exact was the Coq tactic I wanted to intercept but I was wrong I believe. My notes on exact:
- exact p. (assuming p has type U).
- closes a proof if the goal term T (i.e. a Type) matches the type of the given term p.
- succeeds iff T and U are convertible (basically, intuitively if they unify https://coq.inria.fr/refman/language/core/conversion.html#conversion-rules since are saying if T is convertible to U)
conversion seems to be equality check not really unification i.e. it doesn't try to solve a system of symbolic equations.
Appendix 3: Recall unification
brief notes:
- unification https://en.wikipedia.org/wiki/Unification_(computer_science)
- an algorithm that solves a system of equations between symbolic expressions/terms
- i.e. you want
- cons2( cons1( x, y, ...,) ..., cons3(a, b, c), ... ) = cons1(x, nil)
- x = y
- basically a bunch of term LHS term RHS and want to know if you can make them all equal given the terms/values and variables in them...
- term1 = term2, term3 = term4 ? with some variables perhaps.
- the solution is the substitution of the variables that satisfies all the equations
bounty
I'm genuinely curious about intercepting the apply tactic or call its unification algorithm.
apply indeed solve a unification, according to the document.
The tactic apply relies on first-order unification with dependent types unless the conclusion of the type of term is of the form P (t1 ... tn) with P to be instantiated.
Note that generally, the apply will turn one "hole" to several "hole"s that each cooresponds to a subgoal generated by it.
I have no idea how to access the internal progress of apply and get the substitution it uses.
However, You can call unify t u to do unification maully. you can refer to the official document. As far as I am concerned, the unicoq plugin provides another unification algorithm, and you can use munify t u to find unification between two items, see the Unicoq official repo.
An example of using unify and mutify:
From Unicoq Require Import Unicoq.
Theorem add_easy_0'':
forall n:nat,
0 + n = n.
Proof.
Show Proof.
intros.
Show Proof.
refine ?[my_goal].
Show my_goal.
munify (fun t : nat => eq_refl : 0 + t = t) (fun n : nat => ?my_goal).
(* unify (fun t : nat => eq_refl : 0 + t = t) (fun n : nat => ?my_goal). *)
Qed.
However, I wonder whether I have understand your question correctly.
Do you want to name the goal?
If you want to "extract the actual lambda term that complete the (parial) term". The so-called "lamda term" is the goal at that time, isn't it? If so, why to you want to "extract" it? It is just over there! Do you want to store the current subgoal and name it? If so, the abstract tactic perhaps helps, as mentioned in How to save the current goal / subgoal as an `assert` lemma
For example:
Theorem add_easy_0'':
forall n:nat,
0 + n = n.
Proof.
Show Proof.
intros.
Show Proof.
abstract apply eq_refl using my_name.
Check my_name.
(*my_name : forall n : nat, 0 + n = n*)
Show Proof.
(*(fun n : nat => my_name n)*)
Qed.
Do you want to get the substituion?
Are you asking a substituion that make the goal term and the conclusion of the theorem applied match? For example:
Require Import Arith.
Lemma example4 : 3 <= 3.
Proof.
Show Proof.
Check le_n.
(* le_n : forall n : nat, n <= n *)
apply le_n.
Are you looking forward to get something like n=3? If you want to get such a "substitution", I am afraid the two tactics mentioned above will not help. Writing OCaml codes should be needed.
Do you want store the prove of current goal?
Or are you looking forward to store the proof of the current goal? Perhaps you can try assert, as mentioned in Using a proven subgoal in another subgoal in Coq.

coq: Tactic to replace true hypothesis in 'and' statement

Assumptions:
l: a < d
Goal: (s a /\ a < d) <-> s a
Here, I have an /\ with an assumed statement. The goal just needs l to be applied, but I cant seem to figure out the tactic. Apply, rewrite, and replace don't work.
rewrite would only work if a < d was a notation for an equality or an equivalence relation, but here I assume that is not the case.
tauto automatically solves your goal, as does easy, but I think you were asking for something less automatic.
It's a bit disappointing, but the best non-automatic proof I can come up with is to split your goal:
Goal forall a d s, a < d -> (s a /\ a < d) <-> s a.
Proof.
intros a d s l.
split.
- intros [sa _].
exact sa.
- intros sa.
split.
+ exact sa.
+ exact l.
Qed.
If you're interested in using rewrite for this kind of thing, the MathComp library defines things in ways that make rewrite the most useful tactic, and in particular it would work in a translated version of your goal. But probably the best short-term solution here is to make use of some automation tactics.
Using SSReflect/mathcomp, as mentioned by #ana-borges, one can indeed rewrite the Assumption l (which is what -> does); this can then be followed by a split, with a true in the conjunction.
From mathcomp Require Import all_ssreflect.
Goal forall a d s, a < d -> (s a /\ a < d) <-> s a.
Proof. move=> a d s ->; split=> [[sa _] //|sa]; exact: conj. Qed.
Maybe there is another shorter version, though.
I figured it out -- you just have to run propositional, which will evaluate such tautological logic automatically.

How to leave a goal unfinished in Coq

In Isabelle, you can leave a goal unfinished in two ways:
sorry: will leave your proof and the fact can be used in later proofs.
oops: will leave the proof but the fact cannot be used in later proofs.
Is there similar functionality in Coq that would allow me to leave a goal unfinished to come back later to it? This is useful to sketch the structure of the proof while not filling in all the details. Note that the approach in How to switch the current goal in Coq? modifies the proof structure. That's not what I'm looking for.
You have several ways to terminate a proof in Coq. You probably know Qed which asserts the proof is completed. There is also Defined for when you want the proof to have computational content.
The things you are looking for are:
Admitted which admits the proof, so it can be used later;
Abort which gives up on proving the lemma.
They can be used that way:
Lemma foo : forall n, n = 0.
Proof.
intro n. destruct n.
- reflexivity.
-
Abort.
Lemma bar : forall n, n = n.
Admitted.
In both cases you can have a partial proof script before the Admitted/Abort or none at all.
As #HTNW points out, you can also use the equivalent tactics admit and give_up inside subgoals of the proof. Using those, the proof however has to be concluded using either Admitted or Abort.
Lemma lem : forall A, A + A -> A.
Proof.
intros A h.
destruct h.
- admit.
- give_up.
Admitted.
The partial proof is in any case thrown away with this solution.
If for some reason you want to keep using a partial proof (for instance if you want to compute with it), a common trick is to use an axiom to close the goals that you want to leave for later.
Axiom cheating : forall A, A.
Tactic Notation "cheat" := apply cheating.
Lemma lem : forall A, A + A -> A.
Proof.
intros A h.
destruct h.
- cheat.
- cheat.
Defined. (* This is now ok *)
You have to be careful using that trick though. You can check that a lemma has been proven without axioms using Print Assumptions lem. If it says "closed under context" you're good, otherwise it will lists the axioms it depends on and if cheating appears you know it's not entirely proven.

Proof irrelevance (2)

Let's assume we have two proofs for a simple lemma.
Lemma l1: exists x:nat, x <> 0.
exists 1.
intro.
discriminate.
Defined.
Lemma l2: exists x:nat, x <> 0.
exists 2.
discriminate.
Defined.
Intuitively, I would say that those are two different proofs.
So, can I prove the following lemma?
Lemma l3: l1 <> l2
I suppose this is undecidable.
What happens if we introduce the Univalence Axiom ?
First, a small note on terminology. There is another sense of "undecidable" often used in theoretical computer science to refer to problems of deciding, given an arbitrary element of some set, whether a fixed property holds or not of that element. If there is an algorithm computable by, say, a Turing machine, that correctly answers the question for any possible input, we say the problem is decidable; otherwise, it is undecidable. Your notion of "undecidable" is often referred to as "independence" (The two issues are of course, related. The problem of whether an arbitrary Coq proposition is provable or not is undecidable.)
Now, back to your question. I believe (although I am not entirely sure) that your lemma l3 cannot be proved or refuted in Coq even if you incorporate the univalence axiom. The reason is that the univalence axiom only violates proof irrelevance for a particular kind of proposition: equality assertions. And there is nothing about it that has any obvious consequences for existential quantification. Perhaps some intuition here can help. There is a computationally relevant analog of existential quantification (that is, something that lives in Type) that allows you to prove your principle, independently of assuming univalence:
Lemma l1: { x:nat | x <> 0 }.
exists 1.
intro.
discriminate.
Defined.
Lemma l2: { x:nat | x <> 0 }.
exists 2.
discriminate.
Defined.
Lemma l3: l1 <> l2.
Proof.
intros H. inversion H.
Qed.
However, even if this is possible for this type, it is still safe to assume irrelevance for existential quantification, because Coq's logic prevents us from manipulating its proofs in a way that allow us to extract which witnesses were used.

Why Coq doesn't allow inversion, destruct, etc. when the goal is a Type?

When refineing a program, I tried to end proof by inversion on a False hypothesis when the goal was a Type. Here is a reduced version of the proof I tried to do.
Lemma strange1: forall T:Type, 0>0 -> T.
intros T H.
inversion H. (* Coq refuses inversion on 'H : 0 > 0' *)
Coq complained
Error: Inversion would require case analysis on sort
Type which is not allowed for inductive definition le
However, since I do nothing with T, it shouldn't matter, ... or ?
I got rid of the T like this, and the proof went through:
Lemma ex_falso: forall T:Type, False -> T.
inversion 1.
Qed.
Lemma strange2: forall T:Type, 0>0 -> T.
intros T H.
apply ex_falso. (* this changes the goal to 'False' *)
inversion H.
Qed.
What is the reason Coq complained? Is it just a deficiency in inversion, destruct, etc. ?
I had never seen this issue before, but it makes sense, although one could probably argue that it is a bug in inversion.
This problem is due to the fact that inversion is implemented by case analysis. In Coq's logic, one cannot in general perform case analysis on a logical hypothesis (i.e., something whose type is a Prop) if the result is something of computational nature (i.e., if the sort of the type of the thing being returned is a Type). One reason for this is that the designers of Coq wanted to make it possible to erase proof arguments from programs when extracting them into code in a sound way: thus, one is only allowed to do case analysis on a hypothesis to produce something computational if the thing being destructed cannot alter the result. This includes:
Propositions with no constructors, such as False.
Propositions with only one constructor, as long as that constructor takes no arguments of computational nature. This includes True, Acc (the accessibility predicated used for doing well-founded recursion), but excludes the existential quantifier ex.
As you noticed, however, it is possible to circumvent that rule by converting some proposition you want to use for producing your result to another one you can do case analysis on directly. Thus, if you have a contradictory assumption, like in your case, you can first use it to prove False (which is allowed, since False is a Prop), and then eliminating False to produce your result (which is allowed by the above rules).
In your example, inversion is being too conservative by giving up just because it cannot do case analysis on something of type 0 < 0 in that context. It is true that it can't do case analysis on it directly by the rules of the logic, as explained above; however, one could think of making a slightly smarter implementation of inversion that recognizes that we are eliminating a contradictory hypothesis and adds False as an intermediate step, just like you did. Unfortunately, it seems that we need to do this trick by hand to make it work.
In addition to Arthur's answer, there is a workaround using constructive_definite_description axiom. Using this axiom in a function would not allow to perform calculations and extract code from it, but it still could be used in other proofs:
From Coq Require Import Description.
Definition strange1: forall T:Type, 0>0 -> T.
intros T H.
assert (exists! t:T, True) as H0 by inversion H.
apply constructive_definite_description in H0.
destruct H0 as [x ?].
exact x.
Defined.
Or same function without proof editing mode:
Definition strange2 (T: Type) (H: 0 > 0): T :=
proj1_sig (constructive_definite_description (fun _ => True) ltac: (inversion H)).
Also there's a stronger axiom constructive_indefinite_description that converts a proposition exists x:T, P x (without uniqueness) into a corresponding sigma-type {x:T | P x}.