Conditional rewrite rules with unknowns in condition - isabelle

In my theory I have some bigger definitions, from which I derive some simple properties using lemmas.
My problem is, that the lemmas for deriving the properties are not used by the simplifier and I have to manually instantiate them. Is there a way to make this more automatic?
A minimal example is shown below:
definition complexFact :: "int ⇒ int ⇒ int ⇒ bool" where
"complexFact x y z ≡ x = y + z"
lemma useComplexFact: "complexFact x y z ⟹ x = y + z"
by (simp add: complexFact_def)
lemma example_1:
assumes cf: "complexFact a b c"
shows "a = b + c"
apply (simp add: cf useComplexFact) (* easy, works *)
done
lemma example_2a:
assumes cf: "complexFact a b c"
shows "a - b = c"
apply (simp add: cf useComplexFact) (* does not work *)
oops
lemma example_2b:
assumes cf: "complexFact a b c"
shows "a - b = c"
apply (simp add: useComplexFact[OF cf]) (* works *)
done
lemma example_2c:
assumes cf: "complexFact a b c"
shows "a - b = c"
apply (subst useComplexFact) (* manually it also works*)
apply (subst cf)
apply simp+
done
I found the following paragraph in the reference manual, so I guess I could solve my problem with a custom solver.
However, I never really touched the internal ML part of Isabelle and don't know where to start.
Rewriting does not instantiate unknowns. For example, rewriting alone
can- not prove a ∈ ?A since this requires instantiating ?A . The
solver, however, is an arbitrary tactic and may instantiate unknowns
as it pleases. This is the only way the Simplifier can handle a
conditional rewrite rule whose condition contains extra variables.

The Isabelle simplifier by itself never instantiates unknowns in assumptions of conditional rewrite rules. However, the solvers can do that, and the most reliable one is assumption. So if complex_fact a b c literally appears in the assumptions of the goal (rather than being added to the simpset with simp add: or with [simp]), the assumption solver kicks in and instantiated the unknowns. However, it will only use the first instance of complex_fact in the assumptions. So if there are several of them, it will not try all of them. In summary, it is better to write
lemma
assumes cf: "complexFact a b c"
shows "a = b + c"
using cf
apply(simp add: useComplexFact)
The second problem with your example is that a = b + c with a, b, and c being free is not a good rewrite rule, because the head symbol on the left-hand side is not a constant, but the free variable a. Therefore, the simplifier will not use the equation a = b + c to replace a with b + c, but to replace literal occurrences of the equation a = b + c with True. You can see this preprocessing in the trace of the simplifer (enable it locally with using [[simp_trace]]). That's the reason why example_1 works and the others don't. If you can change your left hand side such that there is a constant as the head symbol, then some decent proof automation should be possible without writing a custom solver.
Further, you can do some (limited) form of forward reasoning by using useComplexFact as a destruction rule. That is,
using assms
apply(auto dest!: useComplexFact)
can also work in some cases. However, this is pretty close to unfolding the definition in terms of scalability.

Related

Isabelle `subst` but replace right side with left side

Suppose the goal is P l, then I can use apply(subst X) where X is of the form l=r
and as a result I obtain P r. Now my question is whether there exists some other tactic like subst but which could use X to change P r into P l.
Here is an example
theorem mul_1_I : "(x::nat) = 1 * x" by (rule sym, rule Nat.nat_mult_1)
theorem "(λ x::nat . x) ≤ (λ x::nat . 2*x)"
using [[simp_trace]]
apply(rule le_funI)
apply(subst mul_1_I)
apply(rule mult_le_mono1)
apply(simp)
done
where
lemma nat_mult_1: "1 * n = n"
Right now I have to first prove this auxiliary lemma mul_1_I which applies sym to nat_mult_1 and only then I can use subst. Would be ideal if I didn't have to create new lemma specifically for this.
You can use the symmetric attribute to derive the swapped fact. For example, if x is of the form l = r, then x [symmetric] is the fact r = l (which is also valid due to the symmetry of =). Therefore, in your particular case you can use subst nat_mult_1 [symmetric] directly and avoid creating your auxiliary lemma.

Why are the following trivial self-equalities needed in the Isabelle/Isar proof?

I am trying to learn isabelle/Isar and make sense out of the following simple proof about mod from Rings.thy. I made a copy of the type class to avoid clashing with the original:
class semiring_modulo1 = comm_semiring_1_cancel + divide + modulo +
assumes div_mult_mod_eq: "a div b * b + a mod b = a"
begin
Then this is the lemma whose proof confuses me. The first line makes sense as it uses a previous theorem and rewrites it by symmetry. But the next two lines look really strange. Each of them states the same thing equals itself (e.g. "a div b = a div b"). They seem useless/meaningless. They do not connect to the q or r in the lemma to prove (q and r were never mentioned in the proof).
lemma mod_div_decomp:
fixes a b
obtains q r where "q = a div b" and "r = a mod b"
and "a = q * b + r"
proof -
from div_mult_mod_eq have "a = a div b * b + a mod b" by simp
moreover have "a div b = a div b" ..
moreover have "a mod b = a mod b" ..
note that ultimately show thesis by blast
qed
My questions are:
Why are these empty equalities necessary (and leaving them out breaks the proof)?
What is the equivalent statement of these in English/Mizar?
Are there alternative ways in Isar to write the proof in a way that is closer to English (e.g. take q = a div b ...)?
They're not necessary. It's just that the rule that (which is produced by the obtains keyword) looks like this:
?q1 = a div b ⟹ ?r1 = a mod b ⟹ a = ?q1 * b + ?r1 ⟹ thesis
This is the usual encoding of existential statements in higher-order logic. The a div b = a div b and a mod b = a mod b simply provides hints to blast about how to instantiate the first two assumptions. However, it seems that this is not really needed, it also works without these two trivial equalities.
In any case, even if I had wanted to give instantiation hints, I probably would have done it with that[of "a div b" "a mod b"] or that[OF refl refl] instead.
That said, I'm not really sure why this lemma exists in the first place. The assumptions seem kind of vacuous.

Proving a basic identity in Isabelle

Consider the following following definition definition phi :: "nat ⇒ nat" where "phi n = card {k∈{0<..n}. coprime n k}" (see also this answer)
How can I then prove a very basic fact, like phi(p)=p-1 for a prime p ? Here is one possible formalization of this lemma, though I'm not sure it's the best one:
lemma basic:
assumes "prime_elem (p::nat) = true"
shows "phi p = p-1"
(prime_elem is defined in Factorial_Ring.thy)
Using try resp. try0 doesn't lead anywhere. (A proof by hand is immediate though, since the GCD between any m less than p and p is 1. But poking around various file didn't turn out to be very helpful, I imagine I have to guess some clever lemma that I have to give auto for the proof to succeed.)
First of all, true doesn't exist. Isabelle interprets this as a free Boolean variable (as you can see by the fact that it is printed blue). You mean True. Also, writing prime_elem p = True is somewhat unidiomatic; just write prime_elem p.
Next, I would suggest using prime p. It's equivalent to prime_elem on the naturals; for other types, the difference is that prime also requires the element to be ‘canonical’, i.e. 2 :: int is prime, but -2 :: int is not.
So your lemma looks like this:
lemma basic:
assumes "prime_elem (p::nat)"
shows "phi p = p - 1"
proof -
Next, you should prove the following:
from assms have "{k∈{0<..p}. coprime p k} = {0<..<p}"
If you throw auto at this, you'll get two subgoals, and sledgehammer can solve them both, so you're done. However, the resulting proof is a bit ugly:
apply auto
apply (metis One_nat_def gcd_nat.idem le_less not_prime_1)
by (simp add: prime_nat_iff'')
You can then simply prove your overall goal with this:
thus ?thesis by (simp add: phi_def)
A more reasonable and robust way would be this Isar proof:
lemma basic:
assumes "prime (p::nat)"
shows "phi p = p - 1"
proof -
have "{k∈{0<..p}. coprime p k} = {0<..<p}"
proof safe
fix x assume "x ∈ {0<..p}" "coprime p x"
with assms show "x ∈ {0<..<p}" by (cases "x = p") auto
next
fix x assume "x ∈ {0<..<p}"
with assms show "coprime p x" by (simp add: prime_nat_iff'')
qed auto
thus ?thesis by (simp add: phi_def)
qed
By the way, I would recommend restructuring your definitions in the following way:
definition rel_primes :: "nat ⇒ nat set" where
"rel_primes n = {k ∈ {0<..n}. coprime k n}"
definition phi :: "nat ⇒ nat" where
"phi n = card (rel_primes n)"
Then you can prove nice auxiliary lemmas for rel_primes. (You'll need them for more complicated properties of the totient function)

equivalence of arithmetic expressions using algebra_simps

In Programming and Proving in Isabelle/HOL there is Exercise 2.4 which suggests to use 'algebra_simps' on simple arithmetic expressions, represented as 'datatype exp'. Could somebody give an example how some simple properties of such expressions could be proven using algebra_simps? For example 'Mult a b = Mult b a'?
In general I am trying to prove equivalence of simple arithmetic expressions represented in similar form (with limited set of operators).
If you have defined your eval function appropriately, you can prove the property you gave in your example like this:
lemma Mult_comm: "eval (Mult a b) x = eval (Mult b a) x"
by simp
algebra_simps is just a collection of basic simplification rules for groups and rings (such as the integers, in this case). They have nothing to do with this particular example. You can look at the lemmas contained by typing thm algebra_simps.
For this particular proof, you don't actually need algebra_simps, because commutativity of integer multiplication is already a default simplifier rule anyway.
So, to show how to use algebra_simps, consider an example where you actually do need them: right distributivity of multiplication:
lemma Mult_distrib_right: "eval (Mult (Add a b) c) x = eval (Add (Mult a c) (Mult b c)) x"
If you just try apply simp on this, you will get stuck with the goal
(eval a x + eval b x) * eval c x =
eval a x * eval c x + eval b x * eval c x
Luckily, the rule algebra_simps(4) is a rule that says just that: thm algebra_simps(4) will show you that this rule is (?a + ?b) * ?c = ?a * ?c + ?b * ?c. Isabelle's simplifier will apply it automatically if you tell it to use the algebra_simps rules, by doing:
apply (simp add: algebra_simps)
instead of
apply simp

Isabelle: adjusting lemma to form required for `rule` method

I define an inductive relation called step_g. Here is one of the inference rules:
G_No_Op:
"∀j ∈ the (T i). ¬ (eval_bool p (the (γ ⇩t⇩s j)))
⟹ step_g a i T (γ, (Barrier, p)) (Some γ)"
I want to invoke this rule in a proof, so I type
apply (rule step_g.G_No_Op)
but the rule cannot be applied, because its conclusion must be of a particular form already (the two γ's must match). So I adapt the rule like so:
lemma G_No_Op_helper:
"⟦ ∀j ∈ the (T i). ¬ (eval_bool p (the (γ ⇩t⇩s j))) ; γ = γ' ⟧
⟹ step_g a i T (γ, (Barrier, p)) (Some γ')"
by (simp add: step_g.G_No_Op)
Now, when I invoke rule G_No_Op_helper, the requirement that "the two γ's must match" becomes a subgoal to be proven.
The transformation of G_No_Op into G_No_Op_helper looks rather mechanical. My question is: is there a way to make Isabelle do this automatically?
Edit. I came up with a "minimal working example". In the following, lemma A is equivalent to A2, but rule A doesn't help to prove the theorem, only rule A2 works.
consts foo :: "nat ⇒ nat ⇒ nat ⇒ bool"
lemma A: "x < y ⟹ foo y x x"
sorry
lemma A2: "⟦ x < y ; x = z ⟧ ⟹ foo y x z"
sorry
theorem "foo y x z"
apply (rule A)
To my knowledge, nothing exists to automate these things. One could probably implement this as an attribute, i.e.
thm A[generalised x]
to obtain something like A2. The attribute would replace every occurence of the variable it is given (i.e. x here) but the first in the conclusion of the theorem with a fresh variable x' and add the premise x' = x to the theorem.
This shouldn't be very hard to implement for someone more skilled in Isabelle/ML than me – maybe some of the advanced Isabelle/ML hackers who read this could comment on the idea.
There is a well-known principle "proof-by-definition", i.e. you write your initial specifications in a way such the the resulting rules are easy to apply. This might occasionally look unexpected to informal readers, but is normal for formalists.
I had similar problems and wrote a method named fuzzy_rule, which can be used like this:
theorem "foo y x z"
apply (fuzzy_rule A)
subgoal "x < y"
sorry
subgoal "x = z"
sorry
The code is available at https://github.com/peterzeller/isabelle_fuzzy_rule

Resources