Isabelle: commands/ syntactic local variables / syntactic abbreviations - isabelle

I want to write multiple formulas that include a common variable combination, but i am lazy so i want to have a syntactic variable.
IE "a + b + c"
"a + b - c"
"a + b + e - a"
Instead of writing "a + b" each time i would like to be abled to write something like:
X == a + b
"X + c"
"X - c"
"X - e - a"
With the same functionality as LaTeX has it with the \include command. it should still identify "X - e - a" as "b - e".

The short answer
The default way is to use abbreviation. This introduces a purely syntactical abbreviation that will be expanded during parsing. If the a and b in your case are fixed terms like 1+2 and 3+4, you can simply do this:
abbreviation "X ≡ (1 + 2) + (3 + 4)"
and then write X + c, X - c, X - e - a. Note also that abbreviations are folded back before printing, i.e. (1 + 2) + (3 + 4) + 5 will be printed as X + 5. If you don't want this, you can use abbreviation (input) instead.
Also note that 5 + (1 + 2) + (3 + 4) will not be printed as 5 + X and is not syntactically the same as 5 + X because addition associates to the left: 5 + (1 + 2) + (3 + 4) is (5 + (1 + 2)) + (3 + 4), whereas 5 + X is 5 + ((1 + 2) + (3 + 4)).
You can also use definition; this introduces a new constant called X. You can unfold the definition using the theorem X_def. But from your question, I gather that you don't want that.
What about variables?
It is not entirely clear from your question, but I guess your situation is something like this:
lemma foo: "P (a + b + c)"
(* some proof *)
lemma bar: "P (a + b - c)"
(* some proof *)
In that case, you cannot use an abbreviation as above, since a and b are variables and you cannot have free variables on the right-hand side of an abbreviation (or a definition). You can, however, locally fix the variables in an anonymous context:
context
fixes a b :: "'a :: ring_1" (* change this type if necessary *)
begin
abbreviation "X ≡ a + b"
lemma foo: "P (X + 3)"
(* some proof *)
lemma bar: "P (X - 3)"
(* some proof *)
end
The lemmas foo and bar are then exported with the fixed free variables a and b generalised to schematic variables in the usual fashion. However, thm foo will be printed as ?P (X (X ?a ?b) 3), which is a bit strange, and, in fact, any occurrence of + will be printed that way, so doing abbreviation (input) is a good idea in any case.
A note on hygiene
Polluting the global namespace with a name as general as this is typically considered bad style. An alternative using a local definition would be this:
context
fixes a b :: "'a :: ring_1"
fixes X defines X_def[simp]: "X ≡ a + b"
begin
lemma foo: "P (X + 3)"
sorry
lemma bar: "P (X - 3)"
sorry
end
Here, X is not merely a syntactical abbreviation, but a new constant whose definition must be unfolded. However, by declaring the definition a simp rule, this unfolding will be done automatically. It will, however, not be re-folded automatically, so you will never see X in the output after using the simplifier on it.
Upon exiting the context, the definition will be unfolded everywhere and disappears, giving you the lemmas you want.

Related

Reification for interpretation functions which use different interpretation functions below quantifiers/lambdas

I'm currently trying use Isabelle/HOL's reification tactic. I'm unable to use different interpretation functions below quantifiers/lambdas. The below MWE illustrates this. The important part is the definition of the form function, where the ter call occurs below the ∀. When trying to use the reify tactic I get an Cannot find the atoms equation error. I don't get this error for interpretation functions which only call themselves under quantifiers.
I can't really reformulate my problem to avoid this. Does anybody know how to get reify working for such cases?
theory MWE
imports
"HOL-Library.Reflection"
begin
datatype Ter = V nat | P Ter Ter
datatype Form = All0 Ter
fun ter :: "Ter ⇒ nat list ⇒ nat"
where "ter (V n) vs = vs ! n"
| "ter (P t1 t2) vs = ter t1 vs + ter t2 vs"
fun form :: "Form ⇒ nat list ⇒ bool"
where "form (All0 t) vs = (∀ v . ter t (v#vs) = 0)" (* use of different interpretation function below quantifier *)
(*
I would expect this to reify to:
form (All0 (P (V 0) (V 0))) []
instead I get an error :-(
*)
lemma "∀ n :: nat . n + n = 0"
apply (reify ter.simps form.simps)
(* proof (prove)
goal (1 subgoal):
1. ∀n. n + n = n + n
Cannot find the atoms equation *)
oops
(* As a side note: the following example in src/HOL/ex/Reflection_Examples.thy (line 448, Isabelle2022) seems to be broken? For me, the reify invocation
doesn't change the goal at all. It uses quantifiers too, but only calls the same interpretation function under quantifiers and also doesn't throw an error,
so at least for me this seems to be unrelated to my problem.
*)
(*
lemma " ∀x. ∃n. ((Suc n) * length (([(3::int) * x + f t * y - 9 + (- z)] # []) # xs) = length xs) ∧ m < 5*n - length (xs # [2,3,4,x*z + 8 - y]) ⟶ (∃p. ∀q. p ∧ q ⟶ r)"
apply (reify Irifm.simps Irnat_simps Irlist.simps Irint_simps)
oops
*)
end

Eisbach term parameters and subgoal_tac

I'm currently trying to get into Eisbach.
How can I achieve that in the subgoal_tac (see below) the value
of the parameter A is used, and that A is not interpreted as some variable name? Is there some general way to do this or would this need special tailoring of the subgoal_tac tactic?
theory Scratch (* Isabelle2019 *)
imports
Main
"HOL-Eisbach.Eisbach"
begin
method test for A :: nat =
subgoal_tac "A = 5"
lemma "True"
apply (test 1)
(*
proof (prove)
goal (2 subgoals):
1. A = 5 ⟹ True
2. A = 5
*)
(* The A has a yellow background in the output pane*)
oops
end
I don't know why it does not work with subgoal_tac. I think I read somewhere that all methods ending with _tac are kind of deprecated now.
As a workaround you could use a Lemma:
method test for A :: nat =
(rule meta_mp[where P="A = 5"])
lemma "True"
apply (test 1)
(*
goal (2 subgoals):
1. 1 = 5 ⟹ True
2. 1 = 5
*)

Revisiting Gauss sum with Isabelle

I'm interested in a variation of the argument that stablishes:
"(∑ i=1..k . i) = k*(k+1) div 2"
We know this follows from a simple induction but the intution is a bit different. A way to see this formula is that if you sum the extremes of the sequence of numbers 1..k you get
1+k = 2 + (k-1) = ...
and then you just multiply the right number of times to get the complete sum.
I would like to reproduce this argument to show the following inequality:
"(∑n = 1..k - 1. cmod (f (int n))) ≤ 2 * (∑n ≤ k div 2. cmod (f (int n)))"
Here I know that cmod (f (int k - n)) = cmod (cnj (f n)) for every n.
Do you see an elegant way of proving this in Isabelle?
The trick to do this proof in an elegant way is to realise that ∑i=1..k. i is the same as ∑i=1..k. k + 1 - i and then adding that to the original sum so that the i cancels. This is a simple re-indexing argument:
lemma "(∑i=1..k. i :: nat) = k * (k + 1) div 2"
proof -
have "(∑i=1..k. i) = (∑i=1..k. k + 1 - i)"
by (rule sum.reindex_bij_witness[of _ "λi. k + 1 - i" "λi. k + 1 - i"]) auto
hence "2 * (∑i=1..k. i) = (∑i=1..k. i) + (∑i=1..k. k + 1 - i)"
by simp
also have "… = k * (k + 1)"
by (simp add: sum.distrib [symmetric])
finally show ?thesis by simp
qed
For the other thing you mentioned, I think that the best way to do this is to first split the sum into the elements less that k div 2 and the rest. Then you can reindex the second sum similarly to the one above. Then the inequality part comes in because you might have one extra leftover element ‘in the middle’ if k is odd and you have to throw that away.
Brief sketch of the important part of the proof:
lemma
assumes "⋀i. f i ≥ 0"
shows "(∑i=1..<k. f (i::nat) :: real) = T"
proof -
(* Separate summation domain into two disjoint parts *)
have "(∑i=1..<k. f i) = (∑i∈{1..k div 2}∪{k div 2<..<k}. f i)"
by (intro sum.cong) auto
(* Pull sum apart *)
also have "… = (∑i∈{1..k div 2}. f i) + (∑i∈{k div 2<..<k}. f i)"
by (subst sum.union_disjoint) auto
(* Reindex the second sum *)
also have "(∑i∈{k div 2<..<k}. f i) = (∑i∈{1..<k - k div 2}. f (k - i))"
by (rule sum.reindex_bij_witness[of _ "λi. k - i" "λi. k - i"]) auto
(* Throw away the element in the middle if k is odd *)
also have "… ≤ (∑i∈{1..k div 2}. f (k - i))"
using assms by (intro sum_mono2) auto
finally have "(∑i=1..<k. f i) ≤ (∑i=1..k div 2. f i + f (k - i))"
by (simp add: sum.distrib)
Figuring out how to do these sum manipulations idiomatically in Isabelle takes some experience. sum.reindex_bij_witness is a very useful rule (as you can see). Things like sum.mono_neutral_left/right can also help a lot.

Isabelle - Nitpick counterexample usage

I would like to complete this proof.
How can I easily/elegantly use the values found by nitpick? (What to write at the ... part?)
Alternatively, how can I use the fact that nitpick found a counterexample to finish the proof?
lemma Nitpick_test: "¬(((a+b) = 5) ∧ ((a-b) = (1::int)))" (is "?P")
proof (rule ccontr)
assume "¬ ?P"
nitpick
(* Nitpicking formula...
Nitpick found a counterexample:
Free variables:
a = 3
b = 2
*)
show "False" by ...
qed
The theorem does not hold as stated, because if a = 3 and b = 2, the statement evaluates to False. For other values of a and b, however, the statement does hold. Thus, as a and b are implicitly universally quantified, you cannot prove the theorem as stated.
If you want to instead prove
theorem "EX a b. a + b = 5 & a - b = (1 :: int)"
you can use rule exI[where x="..."] to provide the witness ... for the existential quantifier, so 3 and 2 in this case.

In isabelle, why is this simplification lemma not being substituted?

I'm working through the Isabelle "Programming and Proving" tutorial, and am coming to Ex2.10, where you have to arrive at an equation discribing the number of nodes in an "exploded" tree.
The approach I've taken to this is to create separate expressions for the internal and leaf nodes in the tree, and am working on a proof for the number of internal nodes in the tree, as such:
lemma dddq: " a>0 ⟶ (nodes_noleaf (explode a b) = (ptser (a - 1) (2::nat)) + ((2 ^ a) * (nodes_noleaf b)))"
apply(induction a)
apply(simp)
apply(simp add:eeei eeed eeej eeek )
and this leaves the proof state as the following:
goal (1 subgoal):
1. ⋀a. 0 < a ⟶ nodes_noleaf (explode a b) = ptser (a - Suc 0) 2 + 2 ^ a * nodes_noleaf b ⟹
Suc (2 * nodes_noleaf (explode a b)) = ptser a 2 + 2 * 2 ^ a * nodes_noleaf b
Now, I also created (and successfully proved) a lemma that should replace the ptser a 2 + 2 * 2 ^ a * nodes_noleaf b with (Suc (2 * ((ptser (a - Suc 0) 2) + 2 ^ a * nodes_noleaf b)))), as such:
lemma eeek: "∀ a b . a>0 ⟶ (((ptser a 2) + 2 * 2 ^ a * nodes_noleaf b) = (Suc (2 * ((ptser (a - Suc 0) 2) + 2 ^ a * nodes_noleaf b))))"
apply(auto)
apply(simp add: ddddd)
done
However, adding this to the list of simplifications for the dddq does nothing, and I don't see the reason why.
Additional definitions..
fun nodes_noleaf:: "tree0 ⇒ nat" where
"nodes_noleaf Tip = 0"|
"nodes_noleaf (Node a b) = (add 1 (add (nodes_noleaf a) (nodes_noleaf b)))"
fun explode:: "nat ⇒ tree0 ⇒ tree0" where
"explode 0 t = t" |
"explode (Suc n) t = explode n (Node t t)"
fun ptser:: "nat ⇒ nat ⇒ nat" where
"ptser 0 b = b^0" |
"ptser a b = b^a + (ptser (a - 1) b)"
Your lemma eeek is a conditional rewrite rule, because it can only be applied when the simplifier can prove that a > 0 holds. In your goal state, however, you do not have the assumption a > 0. The 0 < a is a precondition to the induction hypothesis (--> binds stronger than ==>), which is why simp does not apply the induction hypothesis either.
Since the question does not contain all the definitions of your goal, it is hard to pinpoint the exact reason. Nevertheless, I suggest to drop the assumption a > 0 from dddq and prove a stronger statement.
A comment on style: Try to use the connectives !! and ==> of the natural deduction framework rather than explicit universal quantifiers and -->. The simplifier knows how to convert them back into !! and ==>, but other proof methods do not do this automatically. Thus, using !! and ==> will save you boilerplate proof steps later on.

Resources