In Isabelle, I have defined a function f:'a -> nat where 'a is some algebraic structure that extends a monoid (i.e. a group, semiring, ring, integral domain, field, ...).
I would like to use the output of this function as "coefficients" for my type 'a in other constructs. That is, if x:'a and n:nat, I would like to be able to use some operation ·:'a -> nat -> 'a that allows me to tell Isabelle that n·x = x + x + ... + x.
By searching a bit, I found the "Power.thy" theory and, in a sense, it does what I want. However, it does it for the "multiplicative version" of my problem. This is an issue if I want to change 'a for e.g. the integers. Using it would mean that instead of computing n·x, Isabelle would do x^n. Is there an analogous version to "Power.thy" that does what I want? Or are there any other ways to circumvent this problem?
I do not know of any predefined constant that implements such an operation, but it can easily be implemented by iterating addition, e.g., using comppow on nat:
definition scale :: "nat => 'a => 'a" where
"scale a n = ((plus a) ^^ n) 0"
where plus refers to the addition operation of your structure and 0 is the neutral element. If you are using the arithmetic type classes from Isabelle/HOL, you should add the sort constraint 'a :: monoid to scale's type.
There is also a type class operation scaleR in Complex_Main that implements such a coefficient scaling operation, but it allows real numbers, not only nats, so your structure might not satisfy all the required axioms (type class real_vector).
A quite idiomatic way to express this is multiplication and »of_nat«:
context semiring_1
begin
definition scale :: "nat ⇒ 'a ⇒ 'a"
where "scale n = times (of_nat n)"
lemma [simp]:
"scale 0 a = 0"
"scale (Suc n) a = a + scale n a"
by (simp_all add: scale_def algebra_simps)
lemma
"((plus a) ^^ n) 0 = scale n a"
by (induct n) (simp_all)
end
Related
This question is better explained with an example. Suppose I want to prove the following lemma:
lemma int_inv: "(n::int) - (n::int) = (0::int)"
How I'd informally prove this is something along these lines:
Lemma: n - n = 0, for any integer n and 0 = abs_int(0,0).
Proof:
Let abs_int(a,b) = n for some fixed natural numbers a and b.
--- some complex and mind blowing argument here ---
That means it suffices to prove that a+b+0 = a+b+0, which is true by reflexivity.
QED.
However, I'm having trouble with the first step "Let abs_int(a,b) = n". The let statement doesn't seem to be made for this, as it only allows one term on the left side, so I'm lost at how I could introduce the variables a and b in an arbitrary representation for n.
How may I introduce a fixed reprensentation for a quotient type so I may use the variables in it?
Note: I know the statement above can be proved by auto, and the problem may be sidestepped by rewriting the lemma as "lemma int_inv: "Abs_integ(a,b) - Abs_integ(a,b) = (0::int)". However, I'm looking specifically for a way to prove by introducing an arbitrary representation in the proof.
You can introduce a concrete representation with the theorem int.abs_induct. However, you almost never want to do that manually.
The general method of proving statements about quotients is to first state an equivalent theorem about the underlying relation, and then use the transfer tool. It would've helped if your example wasn't automatically discharged by automation... in fact, let's create our own little int type so that it isn't:
theory Scratch
imports Main
begin
quotient_type int = "nat × nat" / "intrel"
morphisms Rep_Integ Abs_Integ
proof (rule equivpI)
show "reflp intrel" by (auto simp: reflp_def)
show "symp intrel" by (auto simp: symp_def)
show "transp intrel" by (auto simp: transp_def)
qed
lift_definition sub :: "int ⇒ int ⇒ int"
is "λ(x, y) (u, v). (x + v, y + u)"
by auto
lift_definition zero :: "int" is "(0, 0)".
Now, we have
lemma int_inv: "sub n n = zero"
apply transfer
proof (prove)
goal (1 subgoal):
1. ⋀n. intrel ((case n of (x, y) ⇒ λ(u, v). (x + v, y + u)) n) (0, 0)
So, the version we want to prove is
lemma int_inv': "intrel ((case n of (x, y) ⇒ λ(u, v). (x + v, y + u)) n) (0, 0)"
by (induct n) simp
Now we can transfer it with
lemma int_inv: "sub n n = zero"
by transfer (fact int_inv')
Note that the transfer proof method is backtracking — this means that it will try many possible transfers until one of them succeeds. Note however, that this backtracking doesn't apply across separate apply commands. Thus you will always want to write a transfer proof as by transfer something_simple, instead of, say proof transfer.
You can see the many possible versions with
apply transfer
back back back back back
Note also, that if your theorem mentions constants about int which weren't defined with lift_definition, you will need to prove a transfer rule for them separately. There are some examples of that here.
In general, after defining a quotient you will want to "forget" about its underlying construction as soon as possible, proving enough properties by transfer so that the rest can be proven without peeking into your type's construction.
I'm trying to translate the argument I gave in this answer into Isabelle and I managed to prove it almost completely. However, I still need to prove:
"(∑k | k ∈ {1..n} ∧ d dvd k. f (k/n)) =
(∑q | q ∈ {1..n/d}. f (q/(n/d)))" for d :: nat
My idea was to use this theorem:
sum.reindex_bij_witness
however, I cannot instantiate the transformations i,j that relate the sets S,T of the theorem. In principle, the setting should be:
S = {k. k ∈ {1..n} ∧ d dvd k}
T = {q. q ∈ {1..n/d}}
i k = k/d
j q = q d
I believe there is a typing error. Perhaps I should be using div?
First of all, note that instead of gcd a b = 1, you should write coprime a b. That is equivalent (at least for all types that have a GCD), but it is more convenient to use.
Second, I would not write assumptions like ⋀n. F n = …. It makes more sense to write that as a defines, i.e.
lemma
fixes F :: "nat ⇒ complex"
defines "F ≡ (λn. …)"
Third, {q. q ∈ {1..n/d}} is exactly the same as {1..n/d}, so I suggest you write it that way.
To answer your actual question: If what you have written in your question is how you wrote it in Isabelle and n and d are of type nat, you should be aware that {q. q ∈ {1..n/d}} actually means {1..real n / real d}. If n / d > 1, this is actually an infinite set of real numbers and probably not what you want.
What you actually want is probably the set {1..n div d} where div denotes division on natural numbers. This is then a finite set of natural numbers.
Then you can prove the following fairly easily:
lemma
fixes f :: "real ⇒ complex" and n d :: nat
assumes "d > 0" "d dvd n"
shows "(∑k | k ∈ {1..n} ∧ d dvd k. f (k/n)) =
(∑q∈{1..n div d}. f (q/(n/d)))"
by (rule sum.reindex_bij_witness[of _ "λk. k * d" "λk. k div d"])
(use assms in ‹force simp: div_le_mono›)+
A note on div
div and / denote the same function, namely Rings.divide.divide. However, / for historic reasons (and perhaps in fond memory of Pascal), / additionally imposes the type class restriction inverse, i.e. it only works on types that have an inverse function.
In most practical cases, this means that div is a general kind of division operation on rings, whereas / only works in fields (or division rings, or things that are ‘almost’ fields like formal power series).
If you write a / b for natural numbers a and b, this is therefore a type error. The coercion system of Isabelle then infers that you probably meant to write real a / real b and that's what you get.
It's a good idea to look at the output in such cases to ensure that the inferred coercions match what you intended.
Debugging non-matching rules
If you apply some rule (e.g. with apply (rule …)) and it fails and you don't understand why, there is a little trick to find out. If you add a using [[unify_trace_failure]] before the apply, you get an error message that indicates where exactly the unification failed. In this case, the message is
The following types do not unify:
(nat ⇒ complex) ⇒ nat set ⇒ complex
(real ⇒ complex) ⇒ real set ⇒ complex
This indicates that there is a summation over a set of reals somewhere that should be a summation over a set of naturals.
I have defined the following function:
fun count:: "'a ⇒ 'a list ⇒ nat" where
"count a Nil = 0" |
"count a (Cons b xs) = (count a xs)" |
"count a (Cons a xs) = (count a xs) + (Suc 0)"
It should count the number of occurrences of element a in a list with elemnts of the same type with a. I get the following error:
Malformed definition:
Nonlinear patterns not allowed in sequential mode.
⋀a xs. count a (a # xs) = count a xs + Suc 0
With respect to patterns, ‘linear’ means that each free variable only occurs once. In your third line, the pattern on the left-hand side contains a twice, which makes it non-linear. This is not supported by the ‘sequential’ mode of the function package. This is the mode in which you can specify possibly overlapping function equations one after another and the first one that matches is the one that counts. This is also the mode that the ‘fun’ command uses and which is what functional programming languages like Haskell typically do (note that these usually don't allow non-linear patterns either).
You basically have two possibilities here: If you absolutely want to use non-linear patterns, you can write
function count:: "'a ⇒ 'a list ⇒ nat" where
"count a Nil = 0"
| "a ≠ b ⟹ count a (Cons b xs) = (count a xs)"
| "count a (Cons a xs) = (count a xs) + (Suc 0)"
by (metis neq_Nil_conv surj_pair) auto
termination by lexicographic_order
Note that you have to show the fact that the patterns are exhaustive and non-overlapping manually, as well as termination. ‘fun’ is less powerful but does all of these things automatically.
The much easier and better way is to just reformulate your definition in a way that is more palatable to the system's automation:
fun count:: "'a ⇒ 'a list ⇒ nat" where
"count a Nil = 0"
| "count a (Cons b xs) = (count a xs) + (if a = b then 1 else 0)‹›
This is almost always preferable for a variety of reasons (shorter, easier, works better with code generation).
For more information on the function package, consult the documentation. It's a very powerful and versatile tool, but if you can get what you want with only ‘fun’, that's usually the way you want to go.
In Isabelle, I'm trying to do rule induction on mutually recursive inductive definitions. Here's the simplest example I was able to create:
theory complex_exprs
imports Main
begin
datatype A = NumA int
| AB B
and B = NumB int
| BA A
inductive eval_a :: "A ⇒ int ⇒ bool" and eval_b :: "B ⇒ int ⇒ bool" where
eval_num_a: "eval_a (NumA i) i" |
eval_a_b: "eval_b b i ⟹ eval_a (AB b) i" |
eval_num_b: "eval_b (NumB i) i" |
eval_b_a: "eval_a a i ⟹ eval_b (BA a) i"
lemma foo:
assumes "eval_a a result"
shows "True"
using assms
proof (induction a)
case (NumA x)
show ?case by auto
case (AB x)
At this point, Isabelle stops with 'Illegal schematic variable(s) in case "AB"'. Indeed the current goal is ⋀x. ?P2.2 x ⟹ eval_a (AB x) result ⟹ True which contains the assumption ?P2.2 x. Is that the 'schematic variable' Isabelle is talking about? Where does it come from, and how can I get rid of it?
I get the same problem if I try to do the induction on the rules:
proof (induction)
case (eval_num_a i)
show ?case by auto
case (eval_a_b b i)
Again, the goal is ⋀b i. eval_b b i ⟹ ?P2.0 b i ⟹ True with the unknown ?P2.0 b i, and I can't continue.
As a related question: I tried to do the induction using
proof (induction rule: eval_a_eval_b.induct)
but Isabelle doesn't accept this, saying 'Failed to apply initial proof method'.
How do I make this induction go through? (In my actual application, I do actually need induction because the goal is more complex than True.)
Proofs about mutually recursive definitions, be they datatypes, functions or inductive predicates, must be mutually recursive themselves. However, in your lemma, you only state the inductive property for eval_a, but not for eval_b. In the case for AB, you obviously want to use the induction hypothesis for eval_b, but as the lemma does not state the inductive property for eval_b, Isabelle does not know what it is. So it leaves it as a schematic variable ?P2.0.
So, you have to state two goals, say
lemma
shows "eval_a a result ==> True"
and "eval_b b result ==> True"
Then, the method induction a b will figure out that the first statement corresponds to A and the second to B.
The induction rule for the inductive predicates fails because this rule eliminates the inductive predicate (induction over datatypes only "eliminates" the type information, but this is not a HOL formula) and it cannot find the assumption for the second inductive predicate.
More examples on induction over mutually recursive objects can be found in src/HOL/Induct/Common_Patterns.thy.
I am trying to prove the following in Isabelle:
theorem map_fold: "∃h b. (map f xs) = foldr h xs b"
apply (induction xs)
apply auto
done
How can I get the instantiated value of h and b?
An approach that sometimes works for this purpose is to state a schematic lemma:
schematic_lemma "map f xs = foldr ?h xs ?b"
apply (induct xs)
apply simp
...
Methods like simp or rule can instantiate schematic variables during the proof (a result of unification). If you are able to complete the proof, then you can just look at the resulting lemma to see what the final instantiations were.
Beware that schematic variables can be a bit tricky: sometimes simp will instantiate a schematic variable in a way that makes the current goal trivially provable, but simultaneously makes other subgoals unsolvable.
In this specific case, Isabelle is able to instantiate ?b with no problem, but it can't determine ?h by unification. In general, schematic variables with function types are much trickier to handle.
In the end, I did something like what Manuel suggested: First, state a lemma with ordinary variables (lemma "map f xs = foldr h xs b"). Then see where the proof by induction gets stuck, and incrementally refine the statement until it is provable.
One way is to use SOME:
h := SOME h. ∃b. map f xs = foldr h xs b
b := SOME b. map f xs = foldr h xs b
Using your map_fold theorem and some fiddling around with someI_ex, you could prove that with these definitions, map f xs = foldr h xs b does indeed hold.
However, while this logically gives you values of h and b, I expect you will not be very satisfied with them, because you don't actualls see what h and b are; and there is no way (logically) to do that either.
In some cases, you can also formulate a theorem stating “There are f, xs such that no h, b exist with map f xs = foldr h xs b” and get nitpick to find a counterexample for that statement, but this case is too complicated for nitpick, as it would have to find a function on an infinite domain that depends on another function on an infinite domain.
I do not think there is a way for you to actually get the existential witnesses h and b out of the theorem you proved as concrete values. You will just have to find them yourself by inspection of the induction cases and find that they are h = λx xs. f x # xs and b = [].
This is by far the easiest solution.
Update: Proof extraction
Upon re-reading this thread today, I actually remembered that proof extraction does exist in Isabelle. It requires explicit proof terms to be computed for all theorems, so you need to start Isabelle with isabelle jedit -l HOL-Proofs. Then you can do this:
theorem map_fold: "∃h b. (map f xs) = foldr h xs b"
by (induction xs) auto
extract map_fold
This defines you a constant map_fold of type ('a ⇒ 'b) ⇒ 'a list ⇒ ('a ⇒ 'b list ⇒ 'b list) × 'b list, i.e. given a mapping function and a list, it gives you the function and the initial state you have to put into the foldr in order to get the same result. You can look at the definition using thm map_fold_def. Simplifying it a bit, it looks like this:
map_fold f xs =
rec_list (λx xa. default, []) (λx xa H. (λa b. f a # map f xa, default)) xs
This is a bit difficult to read, but you can see the [] and the f a # map f xa.
Unfortunately, proof terms get pretty big, so I doubt this will be of much use for anything more than toy examples.