How do i show in an example with numbers that addition is primitve recursive.
I understand why it is primitive recursive through the proof but I just can't imagine how it works primitive recursively with numbers.
To show that a function φ is primitive recursive, it suffices to provide a finite sequence of primitive recursive functions beginning with the constant, successor and projection functions and terminating with φ such that each function is constructed from prior functions by composition and primitive recursion. The primitive recursive addition function is defined
add(0,x) = φ(x)
add(n + 1,x) = ψ(n,x,add(n,x))
where φ = P[1/1]
ψ = S ∘ P[3/3]
where P[m/n] is the m-ary projection function returning its nth argument for n >= 1 and n <= m. To demonstrate that add is primitive recursive, we must construct φ and ψ from the basic functions:
1. P[1/1] [Axiom]
2. P[3/3] [Axiom]
3. S [Axiom]
4. S ∘ P[3/3] [1,3 Composition]
6. PR(P[1/1],S ∘ P[3/3]) [1,4 Primitive Recursion]
The function φ is provided by the axioms of primitive recursive functions. The function ψ is constructed by composition from the primitive recursive functions S and P[3/3] in step (4). Finally, the function add is constructed from φ and ψ in step (6) by primitive recursion. To see how a value is computed by a primitive recursive function such as add, it suffices to systematically substitute the right-hand sides of function definitions where appropriate, then simplify. I've collapsed substitution and simplification of composition in the following example:
add(2,3) = S(P[3/3](1,3,add(1,3))) [Def. ψ]
= S(P[3/3](1,3,S(P[3/3](0,3,add(0,3))))) [Def. ψ]
= S(P[3/3](1,3,S(P[3/3](0,3,P[1/1](3))))) [Def. φ]
= S(P[3/3](1,3,S(P[3/3](0,3,3)))) [Def. P[1/1]]
= S(P[3/3](1,3,S(3))) [Def. P[3/3]]
= S(P[3/3](1,3,4)) [Def. S]
= S(4) [Def. P[3/3]]
= 5 [Def. S]
It's unclear precisely what you're asking, so I gave a general overview of the primitive recursive definition of addition, the proof that addition is primitive recursive, and provided an example computation. If you're still unclear, it might be helpful to perform computations on small values of primitive recursive functions.
Related
The math library for the GNU basic calculator bc defines a few standard primitive functions:
s (x) The sine of x, x is in radians.
c (x) The cosine of x, x is in radians.
a (x) The arctangent of x, arctangent returns radians.
l (x) The natural logarithm of x.
e (x) The exponential function of raising e to the value x.
j (n,x) The bessel function of integer order n of x.
The primitive trigonometric functions make sense, because those are all you need to build all the other trigonometric functions, and natural log and exponential are all you need to do logarithmic/exponential calculations in other bases. But what's up with the Bessel function j? I'm posting this question here in hopes someone more familiar with Bessel functions than I understands what the motivation for including it in a basic calculator math library would be.
Here is a trivial function and a lemma:
fun count_from where
"count_from y 0 = []"
| "count_from y (Suc x) = y # count_from (Suc y) x"
lemma "count_from 3 5 = [3,4,5,6,7]"
It's just an example. The real function is more complicated.
Could you please suggest how to prove such a lemmas?
I redefined the function using tail-recursion and proved the lemma as follows:
fun count_from2 where
"count_from2 y 0 ys = ys"
| "count_from2 y (Suc x) ys = count_from2 (Suc y) x (ys # [y])"
lemma "count_from2 3 5 [] = xs ⟹ xs = [3,4,5,6,7]"
apply (erule count_from2.elims)
apply simp
apply (drule_tac s="xs" in sym)
apply (erule count_from2.elims)
apply simp
apply (drule_tac s="xs" in sym)
apply (erule count_from2.elims)
apply simp
apply (drule_tac s="xs" in sym)
apply (erule count_from2.elims)
by auto
For sure it's not an adequate solution.
I have a several questions:
Is it preferred to define functions using tail-recursion? Does it usually simplifies theorem proving?
Why function simplification rules (count_from.simps or count_from2.simps) can't be applied?
Should I define an introduction rules to prove the first lemma?
Is it possible to apply a function induction rule to prove such a lemmas?
Your question might be better phrased as ‘How do I evaluate a recursively-defined function and get that evaluation as a theorem?’
The answer is that usually the simplifier should do a decent job at evaluating it. The problem here is that numerals like 1, 2, 3 use a binary representation of the natural numbers, whereas the function is defined by pattern matching on 0 and Suc. This is the reason why your simps cannot be applied: they only match on terms of the form count_from ?y 0 or count_from ?y (Suc ?x) and count_from 3 5 is neither.
What you can do to move things along is to use the theorem collection eval_nat_numeral, which simply rewrites numerals like 1, 2, 3 into successor notation:
lemma "count_from 3 5 = [3,4,5,6,7]"
by (simp add: eval_nat_numeral)
Another possibility are the code_simp and eval proof methods which try to prove a statement that is ‘executable’ in some sense by evaluating it and checking that you get True. They both work fine here:
lemma "count_from 3 5 = [3,4,5,6,7]"
by code_simp
The difference between the two is that code_simp uses the simplifier, which gives you a ‘proper’ proof that goes through the Isabelle kernel (but this can be very slow for bigger examples), whereas eval uses the code generator as a trusted oracle and is thus potentially less trustworthy (although I have never seen a problem in practice) but much faster.
As for your other questions: No, I don't see how induction applies here. I don't know what you mean by defining introduction rules (what would they look like?). And tail-recursion does not really have any advantages for proving things – in fact, if you ‘artificially’ make function definitions tail-recursive as you have done for count_from2 you actually make things slightly more difficult, since any properties you want to prove then require additional generalisation before you can prove them by induction. A classic example is normal vs tail-recursive list reversal (I think you can find that in the ‘Programming and Proving’ tutorial).
Also note that a function very much like your count_from already exists in the library: It is called upt :: nat ⇒ nat ⇒ nat list and has custom syntax of the form [a..<b]. It gives you the list of natural numbers between a and b-1 in ascending order. It is a good idea to look at the ‘What's in Main’ document to get an idea for what.
I want to create a function F in Isabelle that is given a formula
formula = pr int | neg formula | imp formula formula
and yields True if the formula is tautological and False otherwise.
For example:
F( φ ⇒ φ ) = True
F( φ ⇒ (ψ ⇒ φ) ) = True
F( ψ ⇒ φ ) = False
Can anyone help me? I find it really difficult to understand Isabelle's documentation and I cannot find such function (which I think that it should already exist).
In any case, if you want to talk about tautology of formulae (or about any semantic property of formulae), you first need to define semantics for your formulae, i.e. a function eval :: formula ⇒ (int ⇒ bool) ⇒ bool (assuming that the pr constructor represents free variables) that takes a formula and a variable assignment and returns whether the formula holds for that assignment or not.
You can define such a function by recursion over the formula using the primrec or fun command. There are many examples for those i the ‘Programming and Proving‘ tutorial on the Isabelle website.
I'd like to avoid brackets in applying a composition of functions to a value. I come up with the idea to compose a function and a value:
julia> ∘(f::Function, x::Number)=f(x)
∘ (generic function with 2 methods)
julia> sqrt ∘ abs ∘ -2
1.4142135623730951
julia> sqrt ∘ abs ∘ (1-3)
1.4142135623730951
My question is how to declare the x argument to accept a "value" which is not a function, so that it does not overwrite the ∘(f::Function, g::Function)
What does compose a function and a value mean? Might it mean scaling with a constant value? e.g. sqrt ∘ abs ∘ x->-2x(this gives you a function, not the result). But it seems what you would like to do is just applying the function to a value, in this case, you could write -2 |> sqrt ∘ abs if you really really hate brackets. I agree with you that overwrite the ∘ is not a good idea, cause it breaks the concept of function composition.
I don't know if it is a good idea but you could probably use this:
∘(f, g) = f(g) # define for all
∘{S<:Function,T<:Function}(f::S, g::T) = (x...)->f(g(x...)) # but not for fnc
edit: I guess you don't want to redefine functionality for any subtype of Function
edit2: needed bigger redefinition
You don't avoid brackets though:
sqrt ∘ abs ∘ (x->2x) ∘ 1
I am currently reading Simon Thompson's The Craft of Functional Programming and when describing recursion, he also mentions a form of recursion called Primitive Recursion.
Can you please explain how this type of recursion is different from "normal" recursive functions?
Here's an example of a primitive recursion function (in Haskell):
power2 n
| n == 0 = 1
| n > 0 = 2 * power2(n - 1)
A simplified answer is that primitive recursive functions are those which are defined in terms of other primitive recursive functions, and recursion on the structure of natural numbers. Natural numbers are conceptually like this:
data Nat
= Zero
| Succ Nat -- Succ is short for 'successor of', i.e. n+1
This means you can recurse on them like this:
f Zero = ...
f (Succ n) = ...
We can write your example as:
power2 Zero = Succ Zero -- (Succ 0) == 1
power2 (Succ n) = 2 * power2 n -- this is allowed because (*) is primitive recursive as well
Composition of primitive recursive functions is also primitive recursive.
Another example is Fibonacci numbers:
fib Zero = Zero
fib (Succ Zero) = (Succ Zero)
fib (Succ n#(Succ n' )) = fib n + fib n' -- addition is primitive recursive
Primitive recursive functions are a (mathematician's) natural response to the halting problem, by stripping away the power to do arbitrary unbounded self recursion.
Consider an "evil" function
f n
| n is an odd perfect number = true
| otherwise = f n+2
Does f terminate? You can't know without solving the open problem of whether there are odd perfect numbers. It's the ability to create functions like these that makes the halting problem hard.
Primitive recursion as a construct doesn't let you do that; the point is to ban the "f n+2" thing while still remaining as flexible as possible -- you can't primitive-recursively define f(n) in terms of f(n+1).
Note that just because a function is not primitive recursive does not mean it doesn't terminate; Ackermann's function is the canonical example.
the recursive functions that can only be implemented by do loops are Primitive recursive functions.
http://en.wikipedia.org/wiki/Primitive_recursive_function