Is it possible to turn unification errors into goals in Coq? - coq

I've been working on a formalization for a process calculus in Coq (repository here), and constantly find myself trying to apply a function which fails because of equivalent, but syntactically different, subterms. This often happens because of manipulation of de Bruijn variables. As unification fails, I'll usually just replace misbehaving subterms explictly beforehand and then apply the function I need. A simple code as an example of what I mean:
Require Import Lia.
Goal
forall P: nat -> Prop,
(forall a b c, P (a + (b + c))) ->
forall a b c, P (b + c + a).
Proof.
intros.
(* Unification fails here. *)
Fail apply H.
(* Replace misbehaving subterms explictly. *)
replace (b + c + a) with (a + (b + c)).
- (* Now application succeeds. *)
apply H.
- (* Show now they were the same thing. *)
lia.
Qed.
So, my question is: is there a tactic, or is it possible to write one with ltac, which is similar to apply, but turning unification errors into additional equality goals instead of failing?

applys_eq from Programming Language Foundations' LibTactics will accomplish that. From the documentation (as of Version 6.1 of the book):
applys_eq H helps proving a goal of the form P x1 .. xN from an [sic] hypothesis H that concludes P y1 .. yN, where the arguments xi and yi may or may not be convertible. Equalities are produced for all arguments that don't unify.
The tactic invokes equates on all arguments, then calls applys K, and attempts reflexivity on the side equalities.

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.

Coq auto tacitc fails

I have the following Coq program that tries to prove n <= 2^n with auto:
(***********)
(* imports *)
(***********)
Require Import Nat.
(************************)
(* exponential function *)
(************************)
Definition f (a : nat) : nat := 2^a.
Hint Resolve plus_n_O.
Hint Resolve plus_n_Sm.
(**********************)
(* inequality theorem *)
(**********************)
Theorem a_leq_pow_2_a: forall a, a <= f(a).
Proof.
auto with *.
Qed.
I expected Coq to either succeed or get stuck trying to. But it immediately returns. What am I doing wrong?
EDIT
I added the Hint Unfold f., and increased bound to 100
but I can't see any unfolding done with debug auto 100:
(* debug auto: *)
* assumption. (*fail*)
* intro. (*success*)
* assumption. (*fail*)
* intro. (*fail*)
* simple apply le_n (in core). (*fail*)
* simple apply le_S (in core). (*fail*)
EDIT 2
I'm adding the manual proof to demonstrate its complexity:
(**********************)
(* inequality theorem *)
(**********************)
Theorem a_leq_pow_2_a: forall a, a <= f(a).
Proof.
induction a as[|a' IHa].
- apply le_0_n.
- unfold f.
rewrite Nat.pow_succ_r.
* rewrite Nat.mul_comm.
rewrite Nat.mul_succ_r.
rewrite Nat.mul_1_r.
unfold f in IHa.
rewrite Nat.add_le_mono with (n:=1) (m:=2^a') (p:=a') (q:=2^a').
-- reflexivity.
-- apply Nat.pow_le_mono_r with (a:=2) (b:=0) (c:=a').
auto. apply le_0_n.
-- apply IHa.
* apply le_0_n.
Qed.
The manual proof that you performed explains why auto can't do it. You did a proof by induction and then a few rewrites. The auto tactic does not allow itself this kind of step.
The tactic auto is meant to find a proof if a manual proof only uses apply with a restricted set of theorems. Here the restrict set of theorems is taken from the core hint database. For the sake of brevity, let's assume this database only contains le_S, le_n, plus_n_O and plus_n_Sm.
To simplify, let's assume that we work with the goal a <= 2 ^ a. The head predicate of this statement is _ <= _ so the tactic will only look at theorems whose principal statement is expressed with _ <= _. This rules out
plus_n_O and plus_n_Sm. Your initiative of adding these goes down the drain.
If we look at le_n the statement is forall n : nat, n <= n. If we replace the universal quantification by a pattern variable, this pattern is ?1 < ?1. Does this unify with a <= 2 ^ a? The answer is no. so this theorem won't be used by auto. Now if we look at le_S, the pattern of the principal statement is ?1 <= S ?2. Unifying this pattern with a <= 2 ^ a, this would require ?1 to be a. Now what could be the value of ?2? We need to compare symbolically the expressions 2 ^ a and S ?2. On the left hand side the function symbol is either _ ^ _ or 2 ^ _ depending on how you wish to look at it, but anyway this is not S. So auto recognizes these functions are not the same, the lemma cannot apply. auto fails.
I repeat: when given a goal, auto first only looks at the head symbol of the goal, and it selects from its database the theorems that achieves proofs for this head symbol. In your example, the head symbol is _ <= _. Then it looks only at the conclusion of these theorems, and checks whether the conclusion matches syntactically with the goal at hand. If it does match, then this should provide values for the universally quantified variables of the theorem, and the premises of the theorem are new goals that should be solved at a lower depth. As was mentioned by #Elazar, the depth is limited to 5 by default.
The Hint unfold directive would be useful only if you had made a definition of the following shape:
Definition myle (x y : nat) := x <= y.
Then Hint Unfold myle : core. would be useful to make sure that the database theorems for _ <= _ are also used to proved instances of myle, as in the following example (it fails if we have 4 occurrences of S, because of the depth limitation).
Lemma myle3 (x : nat) : myle x (S (S (S x))).
Proof. auto with core. Qed.
Please note that the following statement is logically equivalent (according to the definition of addition) but not provable by auto. Even if you add Hint unfold plus : core. as a directive, this won't help because plus is not the head symbol of the goal.
Lemma myleplus3 (x : nat) : myle x (3 + x).
Proof.
auto. (* the goal is unchanged. *)
simpl. auto.
Qed.
I often use automatic tactics from Coq, for instance lia, but I always do so because I can predict if the goal is part of the intended scope of the tactic.
From the documentation:
This tactic implements a Prolog-like resolution procedure to solve the current goal. It first tries to solve the goal using the assumption tactic, then it reduces the goal to an atomic one using intros and introduces the newly generated hypotheses as hints. Then it looks at the list of tactics associated to the head symbol of the goal and tries to apply one of them (starting from the tactics with lower cost). This process is recursively applied to the generated subgoals.
Also, see the warning there:
auto uses a weaker version of apply that is closer to simple apply so it is expected that sometimes auto will fail even if applying manually one of the hints would succeed.
And finally, the search depth is limited to 5 by default; you can control it using auto num.
So if at any point, none of the "tactics associated to the head symbol" of the current goal makes any progress, auto will fail. And if auto reaches maximum depth, it will fail.
Note that auto it does not automatically apply the unfold tactic. There's no way to solve a <= f(a) when f is opaque, without further assumptions. If you want, you can use Hint Unfold f or Hint Transparent f.

Extensionally equal predicates and equality of universally quantified applications

I am trying to define a recursive predicate using well-founded fixpoints with the obligation to show F_ext when rewriting with Fix_eq. The CPDT says that most such obligations are dischargeable with straightforward proof automation, but unhappily this does not appear to be so for my predicate.
I have reduced the problem to the following lemma (from Proper (pointwise_relation A eq ==> eq) (#all A)). Is it provable in Coq without additional axioms?
Lemma ext_fa:
forall (A : Type) (f g : A -> Prop),
(forall x, f x = g x) ->
(forall x, f x) = (forall x, g x).
It can be shown with extensionality of predicates or functions, but since the conclusion is weaker than the usual one (f = g) I naively thought it would be possible to produce a proof without using additional axioms. After all, both sides of the equality only involve applications of f and g; how could any intensional differences be discerned?
Have I missed a simple proof or is the lemma unprovable?
You might be interested in this code I wrote a while ago, which includes variants of Fix_eq for various numbers of arguments, and don't depend on function extensionality. Note that you don't need to change Fix_F, and can instead just prove variants of Fix_eq.
To answer the question you asked, rather than solve your context, the lemma you state is called "forall extensionality".
It is present in Coq.Logic.FunctionalExtensionality, where the axiom of function extensionality is used to prove it. The fact that the standard library version uses an axiom to prove this lemma is, at the very least, strong evidence that it is not provable without axioms in Coq.
Here is a proof sketch of that fact. Since Coq is strongly normalizing*, every proof of x = y in the empty context is judgmentally equal to eq_refl. That is, if you can prove x = y in the empty context, then x and y are convertible. Let f x := inhabited (Vector.t (x + 1)) and let g x := inhabited (Vector.t (1 + x)). It is straightforward to prove forall x, f x = g x by induction on x. Therefore, if your lemma were true without axioms, we could get a proof of
(forall x, inhabited (Vector.t (x + 1))) = (forall x, inhabited (Vector.t (1 + x)))
in the empty context, and hence eq_refl ought to prove this statement. We can easily check and see that eq_refl does not prove this statement. So your lemma ext_fa is not provable without axioms.
Note that equality for functions and equality for types are severely under-specified in Coq. Essentially, the only types (or functions) that you can prove equal in Coq are the ones that are judgmentally equal (or, more precisely, the ones that are expressible as two judgmentally equal lambdas applied to provably-equal closed terms). The only types that you can prove not equal are the ones which are provably not isomorphic. The only functions that you can prove not equal are the ones which provably differ on some concrete element of the domain that you provide. There's a lot of space between the equalities that you can prove, and the inequalities you can prove, and you don't get to say anything about things in this space without axioms.
*Coq isn't actually strongly normalizing because there are some issues with coinductives. But modulo that, it's strongly normalizing.

How to return a (intro'd) hypothesis back to the goal formula?

For the proof:
Parameter A B : Prop.
Goal A->B.
intro A.
I get:
1 subgoals
A : A
______________________________________(1/1)
B
How do I return then A back to the goal section? To return to:
1 subgoals
______________________________________(1/1)
A -> B
Use the revert tactic:
revert A.
It is exactly the inverse of intro, cf. the reference manual.
You can use the revert tactic.
Given Coq's plethora of tactics, each with various corner cases and varying quality of documentation, it's quite common that you won't know which tactic to use.
In such cases, I find it useful to think of your proof as a program (see Curry-Howard Isomorphism) and to ask yourself what term you would have to write to solve your goal. The advantages of this approach is that Coq's term language is easier to learn (because there just aren't that many different kinds of terms) and expressive enough to solve all goals solvable with tactics (sometimes the proofs are more verbose though).
You can use the refine tactic to write your proofs in the term language. The argument of refine is a term with holes _. refine discharges the current goal using the term and generates a subgoal for every hole in the term. Once you know how refine works, all you have to do is to come up with a term that does what you want. For example:
revert a hypothesis h with refine (_ h).
introduce a hypothesis h with refine (fun h => _).
duplicate a hypothesis h with refine ((fun h' => _) h).
Note that Coq's tactics tend to do quite a bit of magic behind the scenes. For example, the revert tactic is "smarter" than the refine above when dealing with dependent variables:
Goal forall n:nat, n >= 0.
intro n; revert n. (* forall n : nat, n >= 0 *)
Restart.
intro n; refine (_ n). (* nat -> n >= 0 *)
Restart.
intro n'; refine ((_ : forall n, n >= 0) n'). (* forall n : nat, n >= 0 *)
Abort.