Idiomatic ways of selecting subterm to rewrite - coq

Suppose we have a conclusion of form: a + b + c + d + e.
We also have a lemma: plus_assoc : forall n m p : nat, n + (m + p) = n + m + p.
What are idiomatic ways to arbitrarily "insert a pair of parentheses" into the term? That is, how can we easily choose where to rewrite if there's more than one available place.
What I generally end up doing is the following:
replace (a + b + c + d + e)
with (a + b + c + (d + e))
by now rewrite <- ?plus_assoc
And while this formulation does state exactly what I want to do,
it gets extremely long-winded for formulations more complicated than "a b c...".

rewrite <- lemma expects lemma to be an equality, that is, a term whose type is of the form something1 = something2. Like with most other tactics, you can also pass it a function that returns an equality, that is, a term whose type is of the form forall param1 … paramN, something1 = something2, in which case Coq will look for a place where it can apply the lemma to parameters to form a subterm of the goal. Coq's algorithm is deterministic, but letting it choose is not particularly useful except when performing repeated rewrites that eventually exhaust all possibilities. Here Coq happens to choose your desired goal with rewrite <- plus_assoc, but I assume that this was just an example and you're after a general technique.
You can get more control over where to perform the rewrite by supplying more parameters to the lemma, to get a more specific equality. For example, if you want to specify that (((a + b) + c) + d) + e should be turned into ((a + b) + c) + (d + e), i.e. that the associativity lemma should be applied to the parameters (a + b) + c, d and e, you can write
rewrite <- (plus_assoc ((a + b) + c) d e).
You don't need to supply all the parameters, just enough to pinpoint the place where you want to apply the lemma. For example, here, it's enough to specify d as the second argument. You can do this by leaving the third parameter out altogether and specifying the wildcard _ as the first parameter.
rewrite <- (plus_assoc _ d).
Occasionally there are identical subterms and you only want to rewrite one of them. In this case you can't use the rewrite family of tactics alone. One approach is to use replace with a bigger term where you pick what you want to change, or event assert to replace the whole goal. Another approach is to use the set tactics, which lets you give a name to a specific occurrence of a subterm, then rely on that name to identify specific subterms, and finally call subst to get rid of the name when you're done.
An alternative approach is to forget about which lemmas to apply, and just specify how you want to change the goal with something like assert or a plain replace … with ….. Then let automated tactics such as congruence, omega, solve [firstorder], etc. find parameters that make the proof work. With this approach, you do have to write down big parts of the goal, but you save on specifying lemmas. Which approach works best depends on where you are on a big proof and what tends to be stable during development and what isn't.

IMO your best option is to use the ssreflect pattern selection language, available in Coq 8.7 or by installing math-comp in earlier versions. This language is documented in the manual: https://hal.inria.fr/inria-00258384
Example (for Coq 8.7):
(* Replace with From mathcomp Require ... in Coq < 8.7 *)
From Coq Require Import ssreflect ssrfun ssrbool.
Lemma addnC n m : m + n = n + m. Admitted.
Lemma addnA m n o : m + (n + o) = m + n + o. Admitted.
Lemma example m n o p : n + o + p + m = m + n + o + p.
Proof. by rewrite -[_ + _ + o]addnA -[m + _ + p]addnA [m + _]addnC.
Qed.

If you don't want to prove a helper lemma, then one of your choices is using Ltac to pattern match on the structure of the equality on your hands. This way you can bind arbitrary complex subexpressions to pattern variables:
Require Import Coq.Arith.Arith.
Goal forall a b c d e,
(a + 1 + 2) + b + c + d + e = (a + 1 + 2) + (b + c + d) + e -> True.
intros a b c d e H.
match type of H with ?a + ?b + ?c + ?d + ?e = _ =>
replace (a + b + c + d + e)
with (a + (b + c + d) + e)
in H
by now rewrite <- ?plus_assoc
end.
Abort.
In the above piece of code ?a stands for a + 1 + 2. This, of course, doesn't improve anything if you are dealing with simple variables, it helps only when you are dealing with complex nested expressions.
Also, if you need to rewrite things in the goal, then you can use something like this:
match goal with
| |- ?a + ?b + ?c + ?d + ?e = _ => <call your tactics here>

Related

how to simplify basic arithmetic in more complex goals

Here's a minimal example of my problem
Lemma arith: forall T (G: seq T), (size G + 1 + 1).+1 = (size G + 3).
I would like to be able to reduce this to
forall T (G: seq T), (size G + 2).+1 = (size G + 3).
by the simplest possible means. Trying simpl or auto immediately does nothing.
If I rewrite with associativity first, that is,
intros. rewrite - addnA. simpl. auto.,
simpl and auto still do nothing. I am left with a goal of
(size G + (1 + 1)).+1 = size G + 3
I guess the .+1 is "in the way" of simpl and auto working on the (1+1) somehow. It seems like I must first remove the .+1 before I can simplify the 1+1.
However, in my actual proof, there is a lot more stuff than the .+1 "in the way" and I would really like to simplify my copious amount of +1s first. As a hack, I'm using 'replace' on individual occurrences but this feels very clumsy (and there are a lot of different arithmetic expressions to replace). Is there any better way to do this?
I am using the ssrnat library.
Thanks.
Coq has a ring and ring_simplify tactic for this kind of work. Sorry for my ssreflect ignorant intros, but this works:
From mathcomp Require Import all_ssreflect.
Lemma arith: forall T (G: seq T), (size G + 1 + 1).+1 = (size G + 3).
Proof.
intros.
ring.
Qed.
There is also a field and field_simplify. For inequalities there are lia and lra, but I am not sure if these work in mathcomp - for lia you might need this (https://github.com/math-comp/mczify) but it might be integrated meanwhile.
There are many lemmas in ssrnat to reason about addition. One possible solution to your problem is the following:
From mathcomp Require Import all_ssreflect.
Lemma arith: forall T (G: seq T), (size G + 1 + 1).+1 = (size G + 3).
Proof. by move=> T G; rewrite !addn1 addn3. Qed.
where
addn1 : forall n, n + 1 = n.+1
addn3 : forall n, n + 3 = n.+3 (* := n.+1.+1.+1 *)
You can use the Search command to look for lemmas related to certain term patterns. For instance, Search (_ + 1) returns, among other things, addn1.

What's the difference between logical (Leibniz) equality and local definition in Coq?

I am having trouble understanding the difference between an equality and a local definition. For example, when reading the documentation about the set tactic:
remember term as ident
This behaves as set ( ident := term ) in * and
using a logical (Leibniz’s) equality instead of a local definition
Indeed,
set (ca := c + a) in *. e.g. generates ca := c + a : Z in the context, while
remember (c + a ) as ca. generates Heqca : ca = c + a in the context.
In case 2. I can make use of the generated hypothesis like rewrite Heqca., while in case 1., I cannot use rewrite ca.
What's the purpose of case 1. and how is it different from case 2. in terms of practical usage?
Also, if the difference between the two is fundamental, why is remember described as a variant of set in the documentation (8.5p1)?
You could think of set a := b + b in H as rewriting H to be:
(fun a => H[b+b/a]) (b+b)
or
let a := b + b in
H[b+b/a]
That is, it replaces all matched patterns b+b by a fresh variable a, which is then instantiated to the value of the pattern. In this regard, both H and the rewrited hypotheses remain equal by "conversion".
Indeed, remember is in a sense a variant of set, however its implications are very different. In this case, remember will introduce a new proof of equality eq_refl: b + b = b + b, then it will abstract away the left part. This is convenient for having enough freedom in pattern matching etc... This is remember in terms of more atomic tactics:
Lemma U b c : b + b = c + c.
Proof.
assert (b + b = b + b). reflexivity.
revert H.
generalize (b+b) at 1 3.
intros n H.
In addition to #ejgallego's answer.
Yes, you cannot rewrite a (local) definition, but you can unfold it:
set (ca := c + a) in *.
unfold ca.
As for the differences in their practical use -- they are quite different. For example, see this answer by #eponier. It relies on the remember tactic so that induction works as we'd like to. But, if we replace remember with set it fails:
Inductive good : nat -> Prop :=
| g1 : good 1
| g3 : forall n, good n -> good (n * 3)
| g5 : forall n, good n -> good (n + 5).
Require Import Omega.
The variant with remember works:
Goal ~ good 0.
remember 0 as n.
intro contra. induction contra; try omega.
apply IHcontra; omega.
Qed.
and the variant with set doesn't (because we didn't introduce any free variables to work with):
Goal ~ good 0.
set (n := 0). intro contra.
induction contra; try omega.
Fail apply IHcontra; omega.
Abort.

rewrite works for integer but not for rationals for Coq aac_tactics

I was testing Coq rewrite tactics modulo associativity and commutativity (aac_tactics). The following example works for integer (Z), but generates an error when integers are replaced by rationals (Q).
Require Import ZArith.
Import Instances.Z.
Goal (forall x:Z, x + (-x) = 0)
-> forall a b c:Z, a + b + c + (-(c+a)) = b.
intros H ? ? ?.
aac_rewrite H.
When replacing Require Import ZArith. with Require Import QArith. etc., there is an error:
Error: Tactic failure: No matching occurence modulo AC found.
at aac_rewrite H.
There was a similar inconsistency issue between Z and Q, which turned out to be related to whether the Z/Q scope is open.
But I don't understand why aac rewrite didn't work here. What's the cause of the inconsistency, and how can one make it behave the same for Z and Q?
The AAC_tactics library needs theorems which express associativity, commutativity and so forth. Let's take Qplus_assoc which expresses the associativity law for the rational numbers.
Qplus_assoc
: forall x y z : Q, x + (y + z) == x + y + z
As you can see Qplus_assoc doesn't use =, it uses == to express the connection between the left-hand side and the right-hand side. Rationals are defined in the standard library as pairs of integers and positive numbers:
Record Q : Set := Qmake {Qnum : Z; Qden : positive}.
Since, e.g. 1/2 = 2/4, we need some other way of comparing the rationals for equality (other than = which is the notation for eq). For this reason the stdlib defines Qeq:
Definition Qeq (p q : Q) := (Qnum p * QDen q)%Z = (Qnum q * QDen p)%Z.
with notation
Infix "==" := Qeq (at level 70, no associativity) : Q_scope.
So, in case of rational numbers you might want to rewrite your example to something like this:
Require Import Coq.QArith.QArith.
Require Import AAC_tactics.AAC.
Require AAC_tactics.Instances.
Import AAC_tactics.Instances.Q.
Open Scope Q_scope.
Goal (forall x, x + (-x) == 0) ->
forall a b c, a + b + c + (-(c+a)) == b.
intros H ? ? ?.
aac_rewrite H.
Search (0 + ?x == ?x).
apply Qplus_0_l.
Qed.

Why does use of Coq's setoid_replace "by" clause need an extra idtac?

I encountered a strange situation using setoid_replace where a proof step of the form:
setoid_replace (a - c + d) with b by my_tactic
fails with Error: No matching clauses for match goal, but after appending an extra idtac to the tactic:
setoid_replace (a - c + d) with b by (my_tactic; idtac)
the proof succeeds. My understanding of idtac was that it was essentially a no-op. Why does the presence of idtac make a difference here?
Here's the full code. I'm using Coq 8.4pl6 through Proof General.
Require Import QArith.
Open Scope Q.
Lemma rearrange_eq_r a b c d :
a == b -> b + d == a + c -> c == d.
Proof.
intro a_eq_b; rewrite a_eq_b; symmetry; now apply Qplus_inj_l with (z := b).
Qed.
Ltac rearrange :=
match goal with
| [ H : _ == _ |- _ == _ ] => apply rearrange_eq_r with (1 := H); ring
end.
Lemma test_rearrange a b c d e (H0 : e < b) (H1 : b + c == a + d) : e < a - c + d.
Proof.
(* Why is the extra 'idtac' required in the line below? *)
setoid_replace (a - c + d) with b by (rearrange; idtac).
assumption.
Qed.
Note: as Matt observes, idtac doesn't seem to be special here: it seems that any tactic (including fail!) can be used in place of idtac to make the proof succeed.
Thanks to Jason Gross on the Coq bug tracker for explaining this. This has to do with order of evaluation in the Ltac tactic language. In the failing case, the match in rearrange is being applied to the inequality in the immediate goal rather than to the equality generated by setoid_replace. Here's Jason's response on the bug report:
This is because the [match] is evaluated before the [setoid_replace]
is run. It is one of the unfortunate trip-ups of Ltac that things
like [match] and [let ... in ...] are evaluated eagerly until a
statement with semicolons, or other non-match non-let-in statement is
reached. If you add [idtac; ] before the [match] in [rearrange], your
problem will go away.

Fail to use let-destruct for tuple in Coq

I'm a new user for Coq. I have defined some functions:
Definition p (a : nat) := (a + 1, a + 2, a + 3).
Definition q :=
let (s, r, t) := p 1 in
s + r + t.
Definition q' :=
match p 1 with
| (s, r, t) => s + r + t
end.
I'm trying to destruct the result of p into a tuple representation. However coqc complains on q:
Error: Destructing let on this type expects 2 variables.
while q' can pass the compilation. If I change p to return a pair(a + 1, a + 2), the corresponding q and q' both work fine.
Why let-destruct only allows pair? Or have I made any error in syntax? I've checked with Coq manual but found no clue.
Thank you!
What is a bit confusing in Coq is that there are two different forms of destructing let. The one you are looking for needs a quote before the pattern:
Definition p (a : nat) := (a + 1, a + 2, a + 3).
Definition q :=
let '(s, r, t) := p 1 in
s + r + t.
Prefixing the pattern with a quote allows you to use nested patterns and use user-defined notations in them. The form without the quote only works with one-level patterns, and doesn't allow you to use notations, or refer to constructor names in your patterns.