Proving the cardinality of a finite set - isabelle

How can I prove in Isabelle the simple lemma cd : "card {m∈ℕ. m <4} = 4" statement?
auto doesn't help me and oddly sledgehammer times out (even if I use different values on the right-hand side, like 3 or 5 to make sure that I haven't overlooked some technical Isabelle details which perhaps actually make the cardinaliy evaluate to one of these numbers.)
I have the impression that I have to use some lemmas (or get inspiration) from Set_Interval.thy as there sets of these kind are extensively used, but so far I didn't manage to make progress.

Just wanted to add, that if you rewrite your lemma to "card {m::nat. m < n} = n", Isabelle has no problem proving it.
*Edited, thanks Manuel.

The problem with your statement is that it is not true. Look at the definition of ℕ with thm Nats_def: ℕ = range of_nat
of_nat is the canonical homomorphism from the naturals into a semiring_1, i.e. a semiring that has a 1. The definition of ℕ basically says that ℕ consists of all the elements of the ring that are of the form of_nat n for a natural number n. If you look at the type of {m∈ℕ. m <4}, you will see that it is 'a, or if you do a declare [[show_sorts]] before it, 'a :: {ord, semiring_1}, i.e. a semiring with a 1 and some kind of ordering on it. This ordering does not have to be compatible with the ring structure, nor does it have to be linear.
You may think that the set you defined is always the set {0, 1, 2, 3}, but because the ordering is not required to be compatible with the ring structure, this is not the case. The ordering could be trivially true, so you'll get all natural numbers.
Furthermore, even when the set is {0, 1, 2, 3}, its cardinality is not necessarily 4. (Think of the ring ℤ/2ℤ – then that set will be equal to {0, 1}, so the cardinality is 2)
You will probably want to restrict the type of your expression a bit. I think the right type class here is linordered_semidom, i.e. a semiring with a 1, no zero divisors, and a linear ordering that is compatible to the ring structure. Then you can do:
lemma cd : "card {m∈ℕ. m < (4 :: 'a :: linordered_semidom)} = 4"
proof -
have "{m∈ℕ. m < (4 :: 'a)} = {m∈ℕ. m < (of_nat 4 :: 'a)}" by simp
also have "… = of_nat ` {m. m < 4}"
unfolding Nats_def by (auto simp del: of_nat_numeral)
also have "card … = 4" by (auto simp: inj_on_def card_image)
finally show ?thesis .
qed
The proof is a bit ugly considering how intuitively obvious the proposition is; the solution here is not to write down the set you want to describe in this relatively inconvenient way in the first place. It takes a bit of experience to know how to write things in a convenient way.

Related

Case analysis on a premise Isabelle

I have the following proof state:
1. ⋀i is s stk stack.
(⋀stack.
length (exec is s stack) = n' ⟹
length stack = n ⟹ ok n is n') ⟹
length (exec (i # is) s stack) = n' ⟹
length stack = n ⟹ ok n (i # is) n'
How do I perform a case split on i? Where i is of type:
datatype instr = LOADI val | LOAD vname | ADD
I'm doing this for exc 4.7 of concrete semantics so this should be possible to do with tactics.
If anything you should use cases i rule: instr.cases, but that will not work here because i is not a fixed variable but a bound variable. Also, the rule: instr.cases is not really needed because Isabelle will use that rule by default anyway.
Doing a case distinction on a bound variable without fixing it first is kind of discouraged; that said, it can be done by doing apply (case_tac i) instead of apply (cases i). But as I said, this is not the nice way to do it.
A more proper way to do it is to explicitly fix i using e.g. the subgoal command:
subgoal for i is s stk stack
apply (cases i)
An even better way would probably be to use a structured Isar proof instead.
However, I don't think the subgoal command or Isar proofs are something that you know about at this stage of the Concrete Semantics book, so my guess would be that there is a nicer way to do the proof where you don't have to do any manual case splitting.
Most probably you are doing an induction on the list of instructions; it would probably be better to do an induction on the predicate ok instead. But then again: Where is that predicate ok? I don't see it in your assumptions. It's hard to say what's going on there without knowing how you defined ok and what lemma you are trying to prove exactly and what tactics you applied already.

request clarification on appearance of apparent real coercion in theory involving natural numbers in Isabelle theory

I am examining the following theory in Isabelle2020 /jEdit:
theory Sqrt
imports Complex_Main "HOL-Computational_Algebra.Primes"
begin
theorem
assumes "prime (p::nat)"
shows "sqrt p ∉ ℚ"
proof
from ‹prime p› have p: "1 < p" by (simp add: prime_nat_iff)
assume "sqrt p ∈ ℚ"
then obtain m n :: nat where
n: "n ≠ 0" and sqrt_rat: "¦sqrt p¦ = m / n"
and "coprime m n" by (rule Rats_abs_nat_div_natE)
[we omit the remainder of the proof]
The Output pane shows proof state:
have (⋀m n. n ≠ 0 ⟹ ¦sqrt (real p)¦ = real m / real n ⟹ coprime m n ⟹ ?thesis) ⟹ ?thesis
proof (state)
this:
n ≠ 0
¦sqrt (real p)¦ = real m / real n
coprime m n
goal (1 subgoal):
1. sqrt (real p) ∈ ℚ ⟹ False
My question is: Are those appearances of "real" a type coercion? I have read Chapter 8 discussing types in the so-called tutorial that accompanies the Isabelle distribution (title A Proof Assistant for Higher-Order Logic). I read Florian Haftman's document title Isabelle/HOL type-class hierarchy (also part of the Isabelle distribution). The rule used in the theory statements above, Rats_abs_nat_div_natE, is a lemma in the Real.thy theory.
I chased down the reference in that theory file and looked at §8.4.5 in A Proof
Assistant for Higher-Order Logic where I found that The natural
number type nat is a linearly ordered semiring, type int is an ordered ring,
and type real is an ordered field. Properties may not hold for a particular class, e.g., no abstract properties involving subtraction hold for type nat (since, of course, one might end up with a negative number, which would not be a natural number). Instead specific theorems are provided addressing subtraction on the type nat. More to the point, “all abstract properties involving division require a field." (A Proof Assistant for Higher-Order Logic.)
So, are we are seeing here a quotient type being used to lift a division of natural or integer types to the abstract real type in order to satisfy the field
requirement (see §11.9 The Isabelle/Isar Reference Manual)? The quotient type real is created from the equivalence relation definition realrel in the Real.thy file.
I was surprised to see real terms in a proof depending on primes, positive integers, and rational numbers and wanted to assure that I had at least gotten close to the explanation why this is occuring in the Isabelle proof.
The function sqrt is only defined over reals. Therefore, you need to convert its argument p from nat to real. There is a coercion that does that automatically for you; hence the real function you can.
After that, the only way to type m/n is real m / real n.
Generally, overloaded syntax is a problematic for proof assistants. For example, 2/3 on paper can be the rational number Fract 2 3 in Isabelle, the real number 2/3, or the inverse of 3 in a F_5 multiplied by two, or something else.
In Isabelle this is solved by (to a certain extend) avoiding overloading and using different notations.

Isabelle/HOL restrict codomain

I am sorry for asking so many Isabelle questions lately. Right now I have a type problem.
I want to use a type_synonym introduced in a AFP-theory.
type_synonym my_fun = "nat ⇒ real"
I have a locale in my own theory where:
fixes n :: nat
and f :: "my_fun"
and A :: "nat set"
defines A: "A ≡ {0..n}"
However, in my use case the output of the function f is always a natural number in the set {0..n}. I want to impose this as a condition (or is there a better way to do it?). The only way I found was to:
assumes "∀v. ∃ i. f v = i ∧ i ∈ A"
since
assumes "∀v. f v ∈ A"
does not work.
If I let Isabelle show me the involved types it seems alright to me:
∀v::nat. ∃i::nat. (f::nat ⇒ real) v = real i ∧ i ∈ (A::nat set)
But of course now I cannot type something like this:
have "f ` {0..10} ⊆ A"
But I have to prove this. I understand where this problem comes from. However, I do not know how to proceed in a case like this. What is the normal way to deal with it? I would like to use my_fun as it has the same meaning as in my theory.
Thank you (again).
If you look closely at ∀v::nat. ∃i::nat. (f::nat ⇒ real) v = real i ∧ i ∈ (A::nat set), you will be able to see the mechanism that was used for making the implicit type conversion between nat and real: it is the abbreviation real (this invokes of_nat defined for semiring_1 in Nat.thy) that appears in the statement of the assumption in the context of the locale.
Of course, you can use the same mechanism explicitly. For example, you can define A::real set as A ≡ image real {0..n} instead of A::nat set as A ≡ {0..n}. Then you can use range f ⊆ A instead of assumes "∀v. ∃ i. f v = i ∧ i ∈ A”. However, I doubt that there is a universally accepted correct way to do it: it depends on what exactly you are trying to achieve. Nonetheless, for the sake of the argument, your locale could look like this:
type_synonym my_fun = "nat ⇒ real"
locale myloc_basis =
fixes n :: nat
abbreviation (in myloc_basis) A where "A ≡ image real {0..n}"
locale myloc = myloc_basis +
fixes f :: "my_fun"
assumes range: "range f ⊆ A"
lemma (in myloc) "f ` {0..10} ⊆ A"
using range by auto
I want to impose this as a condition (or is there a better way to do
it?).
The answer depends on what is known about f. If only a condition on the range of f is known, as the statement of your question seems to suggest, then, I guess, you can only state is as an assumption.
As a side note, to the best of my knowledge, defines is considered to be obsolete and it is best to avoid using it in the specifications of a locale: stackoverflow.com/questions/56497678.

Isabelle - exI and refl behavior explanation needed

I am trying to understand the lemma below.
Why is the ?y2 schematic variable introduced in exI?
And why it is not considered in refl (so: x = x)?
lemma "∀x. ∃y. x = y"
apply(rule allI) (* ⋀x. ∃y. x = y *)
thm exI (* ?P ?x ⟹ ∃x. ?P x *)
apply(rule exI) (* ⋀x. x = ?y2 x *)
thm refl (* ?t = ?t *)
apply(rule refl)
done
UPDATE (because I can't format code in comments):
This is the same lemma with a different proof, using simp.
lemma "∀x. ∃y. x = y"
using [[simp_trace, simp_trace_depth_limit = 20]]
apply (rule allI) (*So that we start from the same problem state. *)
apply (simp only:exI)
done
The trace shows:
[0]Adding rewrite rule "HOL.exI":
?P1 ?x1 ⟹ ∃x. ?P1 x ≡ True
[1]SIMPLIFIER INVOKED ON THE FOLLOWING TERM:
⋀x. ∃y. x = y
[1]Applying instance of rewrite rule "HOL.exI":
?P1 ?x1 ⟹ ∃x. ?P1 x ≡ True
[1]Trying to rewrite:
x = ?x1 ⟹ ∃xa. x = xa ≡ True <-- NOTE: not ?y2 xa or similar!
[2]SIMPLIFIER INVOKED ON THE FOLLOWING TERM:
x = ?x1
[1]SUCCEEDED
∃xa. x = xa ≡ True
So apparently simp and rule handles exI differently. And the remaining question is: what is the mechanical (programmatical) reasoning behind rule's behavior.
When you use rule thm for some fact thm, Isabelle performs higher-order unification of the conclusion of thm with the current goal. If there is a unifier, it is used to instantiate both the goal and the conclusion of the theorem, and then resolution is performed (i.e. the goal is replaced with the assumptions of thm).
This means that:
Schematic variables in the goal can be instantiated by rule through unification
Variables that appear only in the assumptions of thm will not be instantiated by the unification and will therefore remain schematic. That way, you end up with schematic variables in your new goals. Such variables can be seen as existential in some sense, because the conclusion of thm holds if you can prove the assumptions for just one arbitrary value.
In the case of exI, you have ?P ?x ⟹ ∃x. ?P x. When you apply rule exI, the variable ?P is instantiated to λy. x = y, but the variable ?x appears only in the assumptions of exI, so it remains schematic. This means that you can pick any value you want for ?x later on in your proof.
To be more precise, you end up with ⋀x. x = ?y2 x as your goal. You might ask ‘Why not just ⋀x. x = ?y2?’ That would mean that you have to show that x equals some fixed value y2 for all possible values of x. That is obviously not true in general. ⋀x. x = ?y2 x means you have to show that every x equals some y2 that may depend on x – or, equivalently, that there is a function y2 that, when given x, outputs x.
Of course, there is such a function and it is simply the identity function λx. x. That is precisely what ?y2 gets instantiated to when you apply rule refl: the goal x = ?y2 x is unified with the conclusion of refl ?t = ?t and you end up with ?t = x and ?y2 = λx. x, and since refl has no assumptions, this resolution finishes the proof.
I am not entirely sure what you mean with ‘And why it is not considered in refl?’, but I hope that I have answered your questions.
Get a more complete answer from an expert, but I give a short, brief answer to your second part.
The great thing about Isabelle is that it provides many different ways to prove a problem.
Your new question is similar to L.Paulson's comment on FOM: you moved the goal post by switching the question to rule vs. simp:
http://www.cs.nyu.edu/pipermail/fom/2015-October/019312.html
Getting a basic understanding of simp is actually a much easier goal to pursue, or I wouldn't be adding my reponse here.
rule and natural deduction
The use of rule is the use of natural deduction (ND), where most people aren't up to speed on ND. The use of ND requires understanding ND, so questions like your first question can lead to a non-simple answer, because anything informative can't be a one-liner answer, especially due to things like schematic variables (which you asked about), resolution, unification, rewriting, etc.
Do a search on natural deduction and you'll find the standard wiki page about it. There are numerous books on natural deduction, though they get swamped in searches on "logic" due to first-order logic books. A popular book is Logic in Computer Science, 2nd, by Huth and Ryan.
If you study ND, you'll see that exI matches one of the ND rules.
I have yet to take the time to come up to speed on ND, because I keep making progress without having more than a basic understanding of ND.
Sledgehammer, and auto-methods auto, simp, blast, induct, cases, etc., and Sledgehammer's use of some of those, keep me from finding the time to become good with natural decution.
Answer's like M.Eberl's, though not simple explanations, help me absorb a little here and a little there.
Simp, I think of it as simple substitution (rewriting)
The mechanics behind simp is really simple, compared to natural deduction. You define a formula and prove it:
lemma foo [simp]: "left_hand_side = right_hand_side"
In the proof of another theorem, when simp is invoked in one way or another, or foo is unfolded, where there is left_hand_side, it's replaced with right_hand_side. It's just classic mathematical substitution.
I suppose it could also be "rewriting", but I don't know anything about rewriting, other than they talk about it.
There are lots of details about how and whether one should set things up automatically (to prevent looping), like with [simp] or declare foo_def [simp add], but that's just details along the line of normal programming.

Simplify pretty-printing of naturals

Let's say I wrote a function for reversing a list. I want to test it out using the value command, just to assure myself that I probably got it right. But the output looks horrible:
value "reverse [1,8,3]"
> "[1 + 1 + 1, 1 + 1 + (1 + 1) + (1 + 1 + (1 + 1)), 1]" :: "'a list"
If I tell Isabelle to treat those numeric characters as naturals, the output is even worse:
value "reverse [1::nat,8,3]"
> "[Suc (Suc (Suc 0)), Suc (Suc (Suc (Suc (Suc (Suc (Suc (Suc 0))))))), Suc 0]" :: "nat list"
Sometimes I resort to using strings, but that's looks a bit funny with all those apostrophes everywhere:
value "reverse [''1'',''8'',''3'']"
> "[''3'', ''8'', ''1'']" :: "char list list"
Can I instruct Isabelle's pretty-printer to print Suc (Suc (Suc 0)) as 3, and so on? Perhaps by giving some magical incantation to the syntax or translations commands?
Here's my complete example, in case you'd like to paste it into Isabelle:
theory Scratch imports Main begin
fun reverse where
"reverse [] = []"
| "reverse (x#xs) = reverse xs # [x]"
value "reverse [1,8,3]"
value "reverse [1::nat,8,3]"
value "reverse [''1'',''8'',''3'']"
end
Short answer: My first thought is to use type int, since (unlike nat) its code generator setup uses a binary numeral representation by default.
Importing "~~/src/HOL/Library/Code_Target_Nat", as naT suggests, is also a good idea if you don't want to use the Suc representation for type nat.
Explanation: Numerals in Isabelle are encoded using constructors defined in Num.thy; e.g. 5 is an abbreviation for numeral (Bit1 (Bit0 One)). Here One, Bit0 and Bit1 are constructors of type num. numeral is overloaded, and works for any type with a 1 and an associative +. Here are the code equations for numeral:
lemma numeral_code [code]:
"numeral One = 1"
"numeral (Bit0 n) = (let m = numeral n in m + m)"
"numeral (Bit1 n) = (let m = numeral n in m + m + 1)"
If we generate code for 5::'a::numeral, then 1 and + on type 'a are treated as uninterpreted constants, so they remain in the output: (1 + 1) + (1 + 1) + 1.
Generating code for 5::nat works the same, except we do have code for 1 and + on type nat, in terms of Suc. Thus (1 + 1) + (1 + 1) + 1 reduces further to Suc (Suc (Suc (Suc (Suc 0)))).
Type int works differently. The code generator setup in Int.thy uses three constructor functions for type int: Pos and Neg of type num => int, as well as 0. A code_abbrev declaration causes each occurrence of numeral at type num => int to be replaced by Pos during code generation. After the code is run, Pos is then turned back into numeral before Isabelle displays the result. Thus 5::int evaluates to just 5.
Special code setup theories: src/HOL/Library contains a few different theories for customizing code generation for numerals.
"~~/src/HOL/Library/Code_Target_Nat" tells the code generator to use the target language's (e.g. SML or Haskell's) built-in numerals for type nat. For example, 5::nat is usually translated to SML as numeral (Bit1 (Bit0 One)); however, with this library loaded it gets translated as 5 in SML. Results of value are translated back into the Isabelle numeral representation afterward.
"~~/src/HOL/Library/Code_Target_Int" is the same, but for type int instead of nat.
"~~/src/HOL/Library/Code_Target_Numeral" simply loads both of the previous two libraries. It only affects types nat and int, not any other types in class numeral.
"~~/src/HOL/Library/Code_Binary_Nat" configures nat in the same style as the default code setup for int: with constructors 0 and nat_of_num::num => nat and a code_abbrev declaration. With this library, value "5::nat" also returns 5.
Note: I hope my answer here doesn't prevent Brian Huffman or Florian Haftmann from giving an answer. If it does, that would be a bad thing. Hopefully, I'm just doing some gofer work to set either of them up.
Short answer 1: The pertinent mailing list email is Re: [isabelle] value no longer pretty-prints numbers of type nat.
Short answer 2: A solution for nat is to import "~~/src/HOL/Library/Code_Target_Nat". Because I'm not clear on the details of how numeral and num are completely tied into HOL at the low level, the solution I'm giving you is not necessarily a good and final solution.
A big part of my answer here is partly to say, before Brian Huffman or Florian Haftmann get here, who are the authors of Num.thy, "I'm interested in this too, because it's related to numeral, which is a powerful part of HOL. The more info I have about the intricacies of using numeral, the better".
Basically, they made a design choice change for Isabelle2013-1, which is to have the default for nat and numeral be represented in a successor form. That's what the mailing list email is about.
If you use declare[[show_sorts=true]], you will see that your value "reverse [1,8,3]" is using type class numeral. I mention that because I've been putting a lot of effort into trying to learn about numeral, and even with concrete types, such as nat and int, the use of constants such as 44 and 5 involve numeral, at least for the input. And even with a concrete type like nat, numeral can be involved in simp rules that are being used.
Before saying more, one way to get nice nat output for value "reverse [1::nat,8,3]" is, again, to use the following import:
"~~/src/HOL/Library/Code_Target_Nat"
The reason I'm interested in your question is because that's just a plug-n-play solution I saw by Andreas Lochbihler in that email.
I can't get value "reverse [1,8,3]" to not use sums of 1 by importing this:
"~~/src/HOL/Library/Code_Target_Numeral"
So, I want to know about how to get numeral to be in that nice form we love to see.
Anyway, numeral is at the core of using number constants. Consider this:
lemma "(3::nat) = z"
using[[simp_trace, simp_trace_depth_limit=100, linarith_trace, rule_trace]]
using[[blast_trace, blast_stats]]
apply simp
oops
Part of the simp trace is this:
[1]SIMPLIFIER INVOKED ON THE FOLLOWING TERM:
3 = z
[1]Procedure "Num.reorient_numeral" produced rewrite rule:
?a1 = ?b1 ≡ ?b1 = ?a1
[1]Applying instance of rewrite rule
?a1 = ?b1 ≡ ?b1 = ?a1
[1]Rewriting:
3 = z ≡ z = 3
If you look at simp traces involving number constants, you'll see that Num rules show up a lot, which come from that most excellent of Num.thy.

Resources