Nested Function Composition - functional-programming

I am unable to wrap my head around the following function composition:
function plus_one(x) {
return x + 1;
}
function trans(f) {
return function(x) {
return 2 * f(2 * x);
};
}
function twice(f) {
return function(x) {
return f(f(x));
}
}
When I attempt to evaluate ((twice)(trans))(plus_one)(1)
This is what I get, assuming plus_one is f
f(2f(2x))=2f(2*2f(2x))=2f(4f(2x)) = 2*(4*(2 + 1)) = 24.
But typing it into the interpreter reveals that it is 20.

((twice)(trans))(plus_one) is trans(trans(plus_one)), and
trans(trans(plus_one)) (1)
—> trans(λx.2 * plus_one(2*x)) (1)
—> λy.2 * ((λx.2 * plus_one(2*x))(2*y) (1)
—> 2 * (λx.2 * plus_one(2*x)) (2*1)
-> 2 * 2 * plus_one(2*2)
-> 2 * 2 * 5
-> 20

It might help to use different parameter names in the different function for not confusing them. f does not always refer to plus_one.
With
plus_one = λ x0 ⇒ x0 + 1;
trans = λ f0 ⇒ λ x1 ⇒ 2 * f0(2 * x1);
twice = λ f1 ⇒ λ x2 ⇒ f1(f1(x2));
we can evaluate
twice(trans)(plus_one)(1)
as
≡ (λ f1 ⇒ λ x2 ⇒ f1(f1(x2)))(trans)(plus_one)(1)
≡ (λ x2 ⇒ trans(trans(x2)))(plus_one)(1)
≡ trans(trans(plus_one)))(1)
≡ (λ f0 ⇒ λ x1 ⇒ 2 * f0(2 * x1))(trans(plus_one)))(1)
≡ (λ x1 ⇒ 2 * trans(plus_one)(2 * x1))(1)
≡ 2 * trans(plus_one)(2 * 1)
≡ 2 * (λ f0 ⇒ λ x1 ⇒ 2 * f0(2 * x1))(plus_one)(2 * 1)
≡ 2 * (λ x1 ⇒ 2 * plus_one(2 * x1))(2 * 1)
≡ 2 * 2 * plus_one(2 * (2 * 1))
≡ 2 * 2 * (λ x0 ⇒ x0 + 1)(2 * (2 * 1))
≡ 2 * 2 * ((2 * (2 * 1)) + 1)
≡ 20

Notation is everything.
plus_one(x) = (x + 1)
trans(f)(x) = 2 * f(2 * x)
twice(f)(x) = f(f(x))
twice(trans)(plus_one)(1)
=
trans(trans(plus_one))(1)
=
2 * (trans(plus_one))(2 * 1)
=
2 * trans(plus_one)(2)
=
2 * (2 * plus_one(2 * 2))
=
2 * (2 * (4 + 1))
=
20
See also:
Combinatory Logic (Stanford Encyclopedia of Philosophy)
Combinatory logic - Wikipedia
Rewriting

Related

Modular arithmetic proofs in agda

I'm trying to prove (n : ℕ) → ∃[ m ] n * n ≡ 3 * m + 2 → ⊥. Typically I would prove this by rewriting it in terms of congruence, and then splitting on each case. There doesn't seem to be a modular arithmetic module in agda-stdlib. How should I implement modular arithmetic or approach this kind of problem without modular arithmetic?
Edit: there has been discussion in the comments that is not quite precise enough to lead me to an answer, so in the hopes of clarifying what cannot fit in the word count I am dumping my various attempts at this problem, some of which inspired by things in the comments.
open Data.Nat.Divisibility.Core
open Data.Nat.Base
open Relation.Binary.Core
open Level using (0ℓ)
open Relation.Binary.PropositionalEquality
open Data.Empty
open Relation.Nullary
open Data.Product using (Σ; _,_; ∃; Σ-syntax; ∃-syntax)
open Relation.Binary.PropositionalEquality.≡-Reasoning
open Data.Nat.Solver
open +-*-Solver
data Z/3 : (n : ℕ) → Set where
Z/3₀ : (n : ℕ) → ∃[ m ] n ≡ 3 * m → Z/3 n
Z/3₁ : (n : ℕ) → ∃[ m ] n ≡ 3 * m + 1 → Z/3 n
Z/3₂ : (n : ℕ) → ∃[ m ] n ≡ 3 * m + 2 → Z/3 n
data _≋_ : Rel ℕ 0ℓ where
3∣|a-b| : ∀ {a b : ℕ} → 3 ∣ ∣ a - b ∣ → a ≋ b
q' : {a : ℕ} → (2 ≋ ((a + 3) * (a + 3))) → (∃[ a' ] 2 ≋ (a' * a'))
q' = {!!}
qq : (n : ℕ) → Z/3 (n * n)
qq zero = Z/3₀ zero (zero , refl)
qq (suc zero) = Z/3₁ 1 (zero , refl)
qq (suc (suc zero)) = Z/3₁ 4 (1 , refl)
qq (suc (suc (suc n))) = Z/3₁ ((3 + n) * (3 + n)) ({!!} , {!!})
q'' : (n : ℕ) → ∃[ m ] n * n ≡ 3 * m + 2 → ⊥
q'' zero (zero , ())
q'' zero (suc fst , ())
q'' (suc zero) (suc zero , ())
q'' (suc zero) (suc (suc fst) , ())
q'' (suc (suc zero)) (suc (suc (suc (suc zero))) , ())
q'' (suc (suc zero)) (suc (suc (suc (suc (suc fst)))) , ())
q'' (suc (suc (suc n))) (suc fst , snd) = q'' n ({!!} , {!!})
sq : {a : ℕ} → Z/3 a → Z/3 (a * a)
sq (Z/3₀ n (m , p)) = Z/3₀ (n * n) (3 * m * m , q) where
q =
begin
n * n
≡⟨ cong (λ e → e * n) p ⟩
(3 * m) * n
≡⟨ cong (λ e → (3 * m) * e) p ⟩
(3 * m) * (3 * m)
≡⟨ solve 1 (λ m' → (con 3 :* m') :* (con 3 :* m') := con 3 :* m' :* m' :+ (con 3 :* m' :* m' :+ (con 3 :* m' :* m' :+ con zero))) refl m ⟩
3 * m * m + (3 * m * m + (3 * m * m + zero))
∎
-- (3 * b + 1) * (3 * b + 1)
-- 9b^2 + 6b + 1
-- 3(3b^2 + 2b) +1
-- (3 * b + 2) * (3 * b + 2)
-- 9b^2 + 12b + 4
-- 3(3b^2+4b+1) + 1
sq (Z/3₁ n (m , p)) = Z/3₁ (n * n) (3 * m * m + 2 * m , q) where
q =
begin
n * n
≡⟨ cong (λ e → e * n) p ⟩
(3 * m + 1) * n
≡⟨ cong (λ e → (3 * m + 1) * e) p ⟩
(3 * m + 1) * (3 * m + 1)
≡⟨ solve 1 (λ m' → (con 3 :* m' :+ con 1) :* (con 3 :* m' :+ con 1) := (m' :+ (m' :+ (m' :+ con 0))) :* m' :+ (m' :+ (m' :+ con 0)) :+ ((m' :+ (m' :+ (m' :+ con 0))) :* m' :+ (m' :+ (m' :+ con 0)) :+ ((m' :+ (m' :+ (m' :+ con 0))) :* m' :+ (m' :+ (m' :+ con 0)) :+ con 0)) :+ con 1 ) refl m ⟩
(m + (m + (m + 0))) * m + (m + (m + 0)) + ((m + (m + (m + 0))) * m + (m + (m + 0)) + ((m + (m + (m + 0))) * m + (m + (m + 0)) + 0)) + 1
∎
sq (Z/3₂ n (m , p)) = Z/3₁ (n * n) (3 * m * m + 4 * m + 1 , {!!})
q : {a : ℕ} → ¬(2 ≋ (a * a))
q {zero} (3∣|a-b| (divides zero ()))
q {zero} (3∣|a-b| (divides (suc quotient) ()))
q {suc zero} (3∣|a-b| (divides zero ()))
q {suc zero} (3∣|a-b| (divides (suc quotient) ()))
q {suc (suc zero)} (3∣|a-b| (divides zero ()))
q {suc (suc zero)} (3∣|a-b| (divides (suc quotient) ()))
q {suc (suc (suc a))} (3∣|a-b| (divides (suc quotient) equality)) = {!!}
Building on Mark's proof outline:
module SOMod where
open import Data.Empty
open import Data.Nat
open import Data.Nat.Properties
open import Data.Nat.DivMod
open import Data.Product
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary.Negation
open ≡-Reasoning
lemma : ∀ m n → (n * n) % 3 ≢ (3 * m + 2) % 3
lemma m n p = contradiction ≡2 (≢2 n)
where
≡2 : ((n % 3) * (n % 3)) % 3 ≡ 2
≡2 = begin
((n % 3) * (n % 3)) % 3 ≡˘⟨ %-distribˡ-* n n 3 ⟩
(n * n) % 3 ≡⟨ p ⟩
(3 * m + 2) % 3 ≡⟨ cong (_% 3) (+-comm (3 * m) 2) ⟩
(2 + 3 * m) % 3 ≡⟨ cong (λ # → (2 + #) % 3) (*-comm 3 m) ⟩
(2 + m * 3) % 3 ≡⟨ [m+kn]%n≡m%n 2 m 3 ⟩
2 % 3 ≡⟨⟩
2 ∎
≢2 : ∀ n → ((n % 3) * (n % 3)) % 3 ≢ 2
≢2 zero = λ ()
≢2 (suc zero) = λ ()
≢2 (suc (suc zero)) = λ ()
≢2 (suc (suc (suc n))) = ≢2 n
proof : ∀ n → ∃[ m ] n * n ≡ 3 * m + 2 → ⊥
proof n (m , p) = contradiction (cong (_% 3) p) (lemma m n)

How to make Isabelle use basic math rules?

I'm trying to learn Isabelle:
theory test
imports Main
begin
datatype tree = Leaf | Node tree tree
fun nodes :: "tree ⇒ nat" where
"nodes Leaf = 1"|
"nodes (Node x y) = 1 + (nodes x) + (nodes y)"
fun explode :: "nat ⇒ tree ⇒ tree" where
"explode 0 t = t" |
"explode (Suc n) t = explode n (Node t t)"
theorem tree_count: "nodes (explode h l) = 2^h + 2^h * (nodes l) - 1"
apply(induction h arbitrary: l)
apply(auto simp add: numeral_eq_Suc)
done
end
However, the theorem is not solved:
Failed to finish proof:
goal (1 subgoal):
1. ⋀h l. (⋀l. nodes (explode h l) = Suc (Suc 0) ^ h + Suc (Suc 0) ^ h * nodes l - Suc 0) ⟹
Suc (Suc 0) ^ h + (Suc (Suc 0) ^ h + Suc (Suc 0) ^ h * (nodes l + nodes l)) - Suc 0 =
Suc (Suc 0) ^ h + Suc (Suc 0) ^ h + (Suc (Suc 0) ^ h + Suc (Suc 0) ^ h) * nodes l - Suc 0
If I'm not mistaken, Isabelle is just not applying x + x = 2*x and a + (b + c) = a + b + c. So I tried adding a lemma to use it with simp:
lemma a: "(x:nat) + x = 2*x"
But this fails with
Type unification failed: Clash of types "_ ⇒ _" and "_ set"
Type error in application: incompatible operand type
Operator: (∈) x :: ??'a set ⇒ bool
Operand: nat :: int ⇒ nat
I assume that it's just not possible to define the type of a variable in a lemma?
Now I could of course redifine the addition - but I guess that's not really best practice.
What would be the best way to solve the initial problem?
Some general approaches to find the right lemmas:
Use the theorem search, either via the query panel or in the text with find_theorems:
find_theorems ‹_ * (_ + _)›
find_theorems name: assoc "_ + _ + _"
find_theorems ‹2 * ?z = ?z + ?z›
Use sledgehammer.
Try the algebra_simps collection as suggested by waldelb (There are also ac_simps, algebra_split_simps, field_simps, and field_split_simps ).
Try to split it up into smaller steps. This helps the simp tool, because simplification can work on both sides of the equation and because you can guide it to the right intermediate steps. The example below is a bit too extreme, doing only one rewrite per step. In general, you can just add intermediary steps where the automatic ones fail.
theorem tree_count: ‹nodes (explode h l) = 2^h + 2^h * (nodes l) - 1›
proof (induction h arbitrary: l)
case 0
show ‹nodes (explode 0 l) = 2 ^ 0 + 2 ^ 0 * nodes l - 1›
by simp
next
case (Suc h)
have ‹nodes (explode (Suc h) l)
= nodes (explode h (Node l l))›
by (subst explode.simps, rule refl)
also have "... = 2 ^ h + 2 ^ h * nodes (Node l l) - 1"
by (subst Suc, rule refl)
also have "... = 2 ^ h + 2 ^ h * (1 + nodes l + nodes l) - 1"
by (subst nodes.simps, rule refl)
also have "... = 2 ^ h + 2 ^ h * (1 + (nodes l + nodes l)) - 1"
by (subst add.assoc, rule refl)
also have "... = 2 ^ h + 2 ^ h * (1 + (2 * nodes l)) - 1"
by (subst mult_2, rule refl)
also have "... = 2 ^ h + (2 ^ h * 1 + 2 ^ h * (2 * nodes l)) - 1"
by (subst distrib_left, rule refl)
also have "... = 2 ^ h + (2 ^ h * 1 + 2 ^ h * 2 * nodes l) - 1"
by (subst mult.assoc, rule refl)
also have "... = 2 ^ h + (2 ^ h * 1 + 2 ^ Suc h * nodes l) - 1"
by (subst power_Suc2, rule refl)
also have "... = (2 ^ h + 2 ^ h * 1) + 2 ^ Suc h * nodes l - 1"
by (subst add.assoc, rule refl)
also have "... = (2 ^ h + 2 ^ h) + 2 ^ Suc h * nodes l - 1"
by (subst mult_1_right, rule refl)
also have "... = 2 ^ h * 2 + 2 ^ Suc h * nodes l - 1"
by (subst mult_2_right, rule refl)
also have "... = 2 ^ Suc h + 2 ^ Suc h * nodes l - 1"
by (subst power_Suc2, rule refl)
finally show ?case .
qed
algebra_simps is a list of common math rules. Adding it solves the problem:
apply(auto simp add: numeral_eq_Suc algebra_simps)

Why is my simproc not activated on the prescribed pattern?

I'm writing a simproc for sine simplifications. Here is an example rewriting sin(x+8pi+pi/2) to cos(x):
lemma rewrite_sine_1:
fixes k' :: int and k :: real
assumes "k ≡ 2 * of_int k'"
shows "sin (x + k*pi + pi/2) ≡ cos (x)"
sorry
ML ‹
fun rewrite_sine ctxt ct =
let
val sum = ct |> Thm.term_of |> dest_comb |> snd
val x = sum |> dest_comb |> fst |> dest_comb |> snd
|> dest_comb |> fst |> dest_comb |> snd
val k = sum |> dest_comb |> fst |> dest_comb |> snd
|> dest_comb |> snd |> dest_comb |> fst
|> dest_comb |> snd |> dest_comb |> snd |> HOLogic.dest_numeral
val rk = SOME (Thm.cterm_of ctxt (HOLogic.mk_number HOLogic.realT k))
val ik2 = SOME (Thm.cterm_of ctxt (HOLogic.mk_number HOLogic.intT (k div 2)))
val cx = SOME( Thm.cterm_of ctxt x)
in
SOME (Thm.instantiate' [] [rk,ik2,cx] #{thm rewrite_sine_1})
end
›
ML ‹
rewrite_sine #{context} #{cterm "sin(x+8*pi+pi/2)"}
›
Up to here everything works but once I set up my simproc:
simproc_setup sine1 ("sin (x + 8 * pi + pi / 2)") = ‹K rewrite_sine›
(* this should be handled by sine1 only, but is not *)
lemma "sin (x + 8 * pi + pi / 2) = cos(x)"
apply simp
sorry
(* this should be handled by sine1 only, but is not *)
lemma "sin(x+8*pi+pi/2) = cos(x)"
apply(tactic ‹simp_tac (put_simpset HOL_basic_ss #{context} addsimprocs [#{simproc sine1}]) 1›)
sorry
I see that it is not really applying even with a basic simpset. Others simprocs is set-up did work, see here.
In the first example, your simproc is not called becaus simprocs (if I remember correctly) are only called after exhausting all ‘normal’ rewriting, and your goal is immediately rewritten to sin (17 * pi / 2 + x) = cos x. Since your simproc does not match that goal anymore, it is not called.
In the second example, your simproc is called (you can verify this by inserting e.g. a val _ = #{print} "foo" into your let block) and it indeed produces a rewrite rule. Unfortunately, this rewrite rule still has the precondition 8 ≡ 2 * real_of_int 4, which simp cannot solve with the very basic simplifier setup you are using, so it fails to apply the rule.
You can find out what's going on there using the simplifier trace:
lemma "sin(x+8*pi+pi/2) = cos(x)"
using [[simp_trace, simp_trace_depth_limit = 100]]
apply(tactic ‹simp_tac (put_simpset HOL_basic_ss #{context} addsimprocs [#{simproc sine1}]) 1›)
Output:
[0]Adding simplification procedure "Scratch.sine1" for
sin (?x + 8 * pi + pi / 2)
[0]Adding simplification procedure "Scratch.sine1" for
sin (?x + 8 * pi + pi / 2)
[1]SIMPLIFIER INVOKED ON THE FOLLOWING TERM:
sin (x + 8 * pi + pi / 2) = cos x
[1]Procedure "Scratch.sine1" produced rewrite rule:
8 ≡ 2 * real_of_int 4 ⟹
sin (x + 8 * pi + pi / 2) ≡ cos x
[1]Applying instance of rewrite rule
8 ≡ 2 * real_of_int 4 ⟹
sin (x + 8 * pi + pi / 2) ≡ cos x
[1]Trying to rewrite:
8 ≡ 2 * real_of_int 4 ⟹
sin (x + 8 * pi + pi / 2) ≡ cos x
[2]SIMPLIFIER INVOKED ON THE FOLLOWING TERM:
8 ≡ 2 * real_of_int 4
[1]FAILED
8 ≡ 2 * real_of_int 4 ⟹
sin (x + 8 * pi + pi / 2) ≡ cos x
[1]IGNORED result of simproc "Scratch.sine1" -- does not match
sin (x + 8 * pi + pi / 2)
If you add the rule 2 * real_of_int 4 ≡ 8 to your simpset, it works as intended.

Quadratic Formula in Isabelle?

I'm looking for a theory file which contains the quadratic formula:
and of course it would be helpful to know the name of the lemma too.
I've already found this paper:
http://www.inf.ed.ac.uk/publications/thesis/online/IM040231.pdf
and I can copy-paste the proof in that, but then I'll have to rewrite it (because it doesn't copy perfectly). It would be better to have something that works straight away: maybe if someone knows where the theory file that matches this paper can be found?
The paper you linked is very old and the proofs from it will not work without major changes.
Here's a short and simple proof of the theorem:
theory Scratch
imports Complex_Main
begin
lemma real_sqrt_unique':
"(x::real) ^ 2 = y ⟹ x = -sqrt y ∨ x = sqrt y"
using real_sqrt_unique[of x y] real_sqrt_unique[of "-x" y]
by (cases "x ≥ 0") simp_all
lemma quadratic_roots_formula:
fixes a b c x :: real
assumes "a ≠ 0"
defines "disc ≡ b^2 - 4 * a * c"
assumes "disc ≥ 0"
shows "a * x^2 + b * x + c = 0 ⟷ x ∈ {(-b - sqrt disc) / (2*a), (-b + sqrt disc) / (2*a)}"
proof -
from assms have "a * x^2 + b * x + c = 0 ⟷ 4 * a * (a * x^2 + b * x + c) = 0"
by simp
also have "4 * a * (a * x^2 + b * x + c) = (2 * a * x + b) ^ 2 - b^2 + 4 * a * c"
by (simp add: algebra_simps power2_eq_square)
also have "… = 0 ⟷ (2 * a * x + b) ^ 2 = disc" by (simp add: disc_def algebra_simps)
also from ‹disc ≥ 0› have "… ⟷ (2 * a * x + b) ∈ {-sqrt disc, sqrt disc}"
by (auto simp: real_sqrt_unique')
also have "… ⟷ x ∈ {(-b - sqrt disc) / (2*a), (-b + sqrt disc) / (2*a)}"
using assms by (auto simp: field_simps)
finally show ?thesis .
qed

In isabelle, why is this simplification lemma not being substituted?

I'm working through the Isabelle "Programming and Proving" tutorial, and am coming to Ex2.10, where you have to arrive at an equation discribing the number of nodes in an "exploded" tree.
The approach I've taken to this is to create separate expressions for the internal and leaf nodes in the tree, and am working on a proof for the number of internal nodes in the tree, as such:
lemma dddq: " a>0 ⟶ (nodes_noleaf (explode a b) = (ptser (a - 1) (2::nat)) + ((2 ^ a) * (nodes_noleaf b)))"
apply(induction a)
apply(simp)
apply(simp add:eeei eeed eeej eeek )
and this leaves the proof state as the following:
goal (1 subgoal):
1. ⋀a. 0 < a ⟶ nodes_noleaf (explode a b) = ptser (a - Suc 0) 2 + 2 ^ a * nodes_noleaf b ⟹
Suc (2 * nodes_noleaf (explode a b)) = ptser a 2 + 2 * 2 ^ a * nodes_noleaf b
Now, I also created (and successfully proved) a lemma that should replace the ptser a 2 + 2 * 2 ^ a * nodes_noleaf b with (Suc (2 * ((ptser (a - Suc 0) 2) + 2 ^ a * nodes_noleaf b)))), as such:
lemma eeek: "∀ a b . a>0 ⟶ (((ptser a 2) + 2 * 2 ^ a * nodes_noleaf b) = (Suc (2 * ((ptser (a - Suc 0) 2) + 2 ^ a * nodes_noleaf b))))"
apply(auto)
apply(simp add: ddddd)
done
However, adding this to the list of simplifications for the dddq does nothing, and I don't see the reason why.
Additional definitions..
fun nodes_noleaf:: "tree0 ⇒ nat" where
"nodes_noleaf Tip = 0"|
"nodes_noleaf (Node a b) = (add 1 (add (nodes_noleaf a) (nodes_noleaf b)))"
fun explode:: "nat ⇒ tree0 ⇒ tree0" where
"explode 0 t = t" |
"explode (Suc n) t = explode n (Node t t)"
fun ptser:: "nat ⇒ nat ⇒ nat" where
"ptser 0 b = b^0" |
"ptser a b = b^a + (ptser (a - 1) b)"
Your lemma eeek is a conditional rewrite rule, because it can only be applied when the simplifier can prove that a > 0 holds. In your goal state, however, you do not have the assumption a > 0. The 0 < a is a precondition to the induction hypothesis (--> binds stronger than ==>), which is why simp does not apply the induction hypothesis either.
Since the question does not contain all the definitions of your goal, it is hard to pinpoint the exact reason. Nevertheless, I suggest to drop the assumption a > 0 from dddq and prove a stronger statement.
A comment on style: Try to use the connectives !! and ==> of the natural deduction framework rather than explicit universal quantifiers and -->. The simplifier knows how to convert them back into !! and ==>, but other proof methods do not do this automatically. Thus, using !! and ==> will save you boilerplate proof steps later on.

Resources