What does Metis: Unused theorems mean in this context? - isabelle

I'm very new to Isabelle, so apologies if this question is poorly formed.
I'm trying to prove the following:
record
Point =
x :: nat
y :: nat
definition
cond :: "Point ⇒ Point ⇒ 𝔹"
where
"cond point1 point2 ≡
abs (x point1 - x point2) = 1 ∨ abs (y point1 - y point2) = 1"
My proof is:
lemma cond_proof : "∃ point1 point2 . cond point1 point2 = True"
sledgehammer
by (metis Point_ext_def abs_division_segment add_diff_cancel_left' cond_def select_convs(1))
This triggers the warning:
Metis: Unused theorems:
Please could someone explain what this means?
And if possible, help me to arrive at a proof which satisfies this condition.
I'm still very new to Isabelle, so all comments are appreciated.

I can comment on some of the practical aspects related to your problem. I believe, the warning is self-explanatory. Normally, you can delete the unused theorems and metis is still able to prove the theorem of interest (see cond_proof_metis in the code listing below).
The lemma cond_proof can be proven by exhibiting two points that satisfy the condition in the lemma. I believe that such an approach may be considered better than using the default proof found by sledgehammer. I have shown how to do this in lemma cond_proof_Isar in the code listing below. There are more compact ways of exhibiting the example. However, given that you are very new to Isabelle, I followed what seemed to me the most natural approach.
I am not certain if you left the command sledgehammer in your proof intentionally. However, in most cases, this is not something that you wish to do. Once a proof is found by sledgehammer, you can remove the command sledgehammer from your proof.
As a side note, I would suggest for you to work through some of the examples and exercises in the books "A Proof Assistant for Higher-Order Logic" by Tobias Nipkow et al and "Concrete Semantics with Isabelle/HOL" by Tobias Nipkow and Gerwin Klein. The aforementioned references contain many examples that are very similar to the problem that you are dealing with.
Unfortunately, I do not know enough about sledgehammer and metis to be able to tell you why the warning is produced upon using the output from sledgehammer to prove the theorem. For further information about sledgehammer and metis see 'A User’s Guide to Sledgehammer for Isabelle/HOL' that was written by Jasmin Christian Blanchette and can be found in the standard Isabelle documentation. Of course, further information about metis can also be found online (e.g. http://www.gilith.com/metis/). Hopefully, someone more knowledgeable than me will be able to provide more details with regard to your query.
section ‹Auxiliary commands›
theory Scratch
imports Complex_Main
begin
record Point =
x :: nat
y :: nat
definition
cond :: "Point ⇒ Point ⇒ bool"
where
"cond point1 point2 ≡
abs (x point1 - x point2) = 1 ∨ abs (y point1 - y point2) = 1"
(*Point_ext_def was part of the output produced by sledgehammer and is
listed as unused by metis. It can be removed with no effect on the
ability of metis to prove the result of interest.*)
lemma cond_proof_metis : "∃ point1 point2 . cond point1 point2 = True"
by (metis (*Point_ext_def*)
abs_1 add_diff_cancel_left' cond_def of_nat_1_eq_iff select_convs(1))
lemma cond_proof_Isar : "∃point1 point2 . cond point1 point2 = True"
proof(intro exI)
define point1::Point where point1: "point1 ≡ ⦇x = 2, y = 0⦈"
define point2::Point where point2: "point2 ≡ ⦇x = 1, y = 0⦈"
show "cond point1 point2 = True"
unfolding cond_def point1 point2 by auto
qed
end
Isabelle version: Isabelle2020

Related

Isabelle termination of function on datatypes containing maps to themselves

Is it possible in Isabelle to define a terminating recursive function f where
f has a single parameter of type t such that values of type t may contain maps to values of type t, and
f performs its recursive calls on all elements in the range of such a map?
For example consider the datatype trie defined in theory Trie_Fun:
datatype 'a trie = Nd bool "'a ⇒ 'a trie option"
and my attempt at a simple function height intended to compute the height of tries (with finitely many outgoing edges):
theory Scratch
imports "HOL-Data_Structures.Trie_Fun"
begin
function height :: "'a trie ⇒ nat" where
"height (Nd _ edges) = (if dom edges = Set.empty ∨ ¬ finite (dom edges)
then 0
else 1 + Max (height ` ran edges))"
by pat_completeness auto
termination (* ??? *)
end
Here lexicographic_order does not suffice to prove the function to be terminating, but so far I have also not been able to formulate any measure on trie (for termination) that does not itself require a similar recursion.
I must admit here that I am not sure whether I have understood datatypes in Isabelle/HOL correctly (i.e., whether a trie of the above definition is actually always of finite height).
Is it possible to show that height terminates?
Based to the comment by Peter Zeller, I was able to prove termination of height by adding (domintros) to the definition and then performing induction on the trie, using the fact height.domintros, resulting in the following termination proof:
function (domintros) height :: "'a trie ⇒ nat" where
"height (Nd _ edges) = (if dom edges = Set.empty ∨ ¬ finite (dom edges)
then 0
else 1 + Max (height ` ran edges))"
by pat_completeness auto
termination apply auto
proof -
fix x :: "'a trie"
show "height_dom x"
proof (induction)
case (Nd b edges)
have "(⋀x. x ∈ ran edges ⟹ height_dom x)"
proof -
fix x assume "x ∈ ran edges"
then have "∃a. edges a = Some x"
unfolding ran_def by blast
then have "∃a. Some x = edges a"
by (metis (no_types))
then have "Some x ∈ range edges"
by blast
then show "height_dom x"
using Nd by auto
qed
then show ?case
using height.domintros by blast
qed
qed

Importing classes into a locale in Isabelle and other related questions

Question
I would like to understand if there exists a simple method for importing classes into locales.
Alternatively, I would like to understand if there is a simple method that would enable me to use multiple types within the assumptions in classes.
I would like to reuse theorems that are associated with certain pre-defined classes in the library HOL for the development of my own locales. However, it seems to me that, at the moment, there are no standard methods that would allow me to achieve this (e.g. see this question - clause 5).
Unfortunately, my problem will require the definition of structures (i.e. locales or classes) with the assumptions that use multiple types. Thus, I would prefer to use locales. However, I would also like to avoid code duplication and reuse the structures that already exist in the library HOL as much as I can.
theory my_theory
imports Complex_Main
begin
(*It is possible to import other classes, establish a subclass relationship and
use theorems from the super classes. However, if I understand correctly, it
is not trivial to ensure that multiple types can be used in the assumptions
that are associated with the subclass.*)
class my_class = order +
fixes f :: "'a ⇒ real"
begin
subclass order
proof
qed
end
lemma (in my_class) property_class: "⟦ x ≤ y; y ≤ z ⟧ ⟹ x ≤ z"
by auto
(*Multiple types can be used with ease. However, I am not sure how (if
it is possible) to ensure that the lemmas that are associated with the
imported class can be reused in the locale.*)
locale my_locale =
less_eq: order less_eq
for less_eq :: "'a ⇒ 'a ⇒ bool" +
fixes f :: "'a ⇒ 'b"
begin
sublocale order
proof
qed
end
sublocale my_locale ⊆ order
proof
qed
(*nitpick finds a counterexample, because, for example, less_eq is treated
as a free variable.*)
lemma (in my_locale) property_locale: "⟦ x ≤ y; y ≤ z ⟧ ⟹ x ≤ z"
by nitpick
end
Proposed solution
At the moment I am thinking about redefining the minimal amount of axioms in my own locales that is sufficient to establish the equivalence between my locales and the corresponding classes in HOL. However, this approach results in a certain amount of code duplication:
theory my_plan
imports Complex_Main
begin
locale partial_order =
fixes less_eq :: "'a ⇒ 'a ⇒ bool" (infixl "≼" 50)
and less :: "'a ⇒ 'a ⇒ bool" (infixl "≺" 50)
assumes refl [intro, simp]: "x ≼ x"
and anti_sym [intro]: "⟦ x ≼ y; y ≼ x ⟧ ⟹ x = y"
and trans [trans]: "⟦ x ≼ y; y ≼ z ⟧ ⟹ x ≼ z"
and less_eq: "(x ≺ y) = (x ≼ y ∧ x ≠ y)"
begin
end
sublocale partial_order ⊆ order
proof
fix x y z
show "x ≼ x" by simp
show "x ≼ y ⟹ y ≼ z ⟹ x ≼ z" using local.trans by blast
show "x ≼ y ⟹ y ≼ x ⟹ x = y" by blast
show "(x ≺ y) = (x ≼ y ∧ ¬ y ≼ x)" using less_eq by auto
qed
sublocale order ⊆ partial_order
proof
fix x y z
show "x ≤ x" by simp
show "x ≤ y ⟹ y ≤ x ⟹ x = y" by simp
show "x ≤ y ⟹ y ≤ z ⟹ x ≤ z" by simp
show "(x < y) = (x ≤ y ∧ x ≠ y)" by auto
qed
lemma (in partial_order) le_imp_less_or_eq: "x ≼ y ⟹ x ≺ y ∨ x = y"
by (simp add: le_imp_less_or_eq)
end
Is the approach that I intend to follow considered to be an acceptable style for the development of a library in Isabelle? Unfortunately, I have not seen this approach being used within the context of the development of HOL. However, I am still not familiar with a large part of the library.
Also, please let me know if any of the information that is stated in the definition of the question is incorrect: I am new to Isabelle.
General comments that are not directly related to the question
Lastly, as a side note, I have noticed that there may be a certain amount of partial code duplication in HOL. In particular, it seems to me that the theories in HOL/Lattice/, HOL/Algebra/Order-HOL/Algebra/Lattice and HOL/Library/Boolean_Algebra resemble the theory in HOL/Orderings-HOL/Lattices. However, I am not certain if the equivalence between these theories was established through the sublocale/subclass relationship (e.g. see class_deps) to a sufficient extent. Of course, I understand that the theories use distinct axiomatisation and the theories in HOL/Algebra/ and HOL/Library/Boolean_Algebra are based on locales. Furthermore, the theories in HOL/Algebra/ contain a certain amount of information that has not been formalised in other theories. However, I would still like to gain a better understanding of why all four theories co-exist in HOL and the relationship between these theories is not always clearly indicated.
A solution to the problem was proposed on the mailing list of Isabelle by Akihisa Yamada and is available at the following hyperlink: link. A copy of the solution (with minor changes to formatting) is also provided below for a reference with the permission of the author.
It should be noted that the proposed solution has also been used in the context of the development of HOL.
Solution proposed by Akihisa Yamada
let me comment to your technical questions as I also tackled the same goal as you. I'll be happy if there's a better solution, though.
lemma (in my_locale) property_locale: "⟦ x ≤ y; y ≤ z ⟧ ⟹ x ≤ z"
by nitpick
Interpreting a class as a locale doesn't seem to import notations, so here "≤" refers to the global one for "ord", which assumes nothing (you can check by ctrl+hover on x etc.).
My solution is to define a locale for syntax and interpret it (sublocale is somehow slow) whenever you want to use the syntax.
locale ord_syntax = ord
begin
notation less_eq (infix "⊑" 50)
notation less (infix "⊏" 50)
abbreviation greater_eq_syntax (infix "⊒" 50) where
"greater_eq_syntax ≡ ord.greater_eq less_eq"
abbreviation greater_syntax (infix "⊐" 50) where
"greater_syntax ≡ ord.greater less"
end
context my_locale begin
interpretation ord_syntax.
lemma property_locale: "⟦ x ⊑ y; y ⊑ z ⟧ ⟹ x ⊑ z" using less_eq.order_trans.
end

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)

Conditional rewrite rules with unknowns in condition

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.

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