proof by contraposition in agda using solvers - math

can you use the Nat solver in agda-stdlib for proof by contradiction? In particular I have a gnarly bit of algebra I'd rather not do by hand, but I'm not sure how to use a solver to make my life easier. For reference this is the result I want to prove:
{n m : ℕ} → 3 + (n + (3 + (n + (3 + (n + n * (3 + n)))))) ≡ 1 + m + (1 + m + (1 + m + 0)) + 2 → ⊥

Related

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.

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.

How to get a big-O, Big-Ω and big-Θ for this function?

I have a function f(n) defined as follows:
f(n) = (n-1)(n+1)lg(n+5)/(n+3)
Here, lg is log2. I'd like to determine the big-O, big-Ω, and big-Θ values for this function. How would I go about approaching this?
Thanks!
Let's start off by simplifying your expression:
f(n) = (n-1)(n+1)lg(n+5)/(n+3)
= ((n2 - 1) lg (n + 5)) / (n + 3)
For now, let's pretend that the additive constants are there. If we delete those constants, we get this function g(n):
g(n) = n2 lg n / n = n lg n
Since we don't expect those constants to make all that much of a difference in the long term, it's reasonable to venture a guess that this function is Θ(n log n). We can prove this by taking the limit of f(n) / n log n as n tends toward infinity. If we get back a nonzero finite value, then we known that f(n) = Θ(n log n).
So let's try it!
limn → ∞ f(n) / n log n
= limn → ∞ (((n2 - 1) lg (n + 5)) / (n + 3)) / n lg n
= limn → ∞ ((n2 - 1) lg (n + 5)) / n lg n (n + 3)
= (limn → ∞ (n2 - 1) / n(n+3)) (limn → ∞ (lg (n + 5) / lg n)
= (limn → ∞ (n2 - 1) / (n2 + n)) (limn → ∞ (lg (n + 5) / lg n)
Both of these limits are degenerate forms of type ∞ / ∞, so we can use l'Hopital's rule and replace each with its derivative:
limn → ∞ (n2 - 1) / (n2 + n)
= limn → ∞ (2n / 2n + 1)
= 1
and
limn → ∞ lg (n + 5) / lg n
= limn → ∞ (1 / (n+5)) / (1 / n)
= limn → ∞ (n / (n+5))
= 1
Therefore, we get
(limn → ∞ (n2 - 1) / (n2 + n)) (limn → ∞ (lg (n + 5) / lg n)
= 1
Consequently, the ratio of f(n) / n lg n tends toward 1 as n goes to infinity, and so we have that f(n) = Θ(n log n), as required. Because of this, we also get that f(n) = O(n log n) and f(n) = Ω(n log n). We also have that f(n) ~ n log n, which is a much stronger claim.
Hope this helps!

How to simplify modulo

I am trying to find the modulo of an expression. All I know is that
(a+b) mod N = ((a mod N) + (b mod N)) mod N
How do I use it to simplify the following modulo operation?
(a - 2*b + 1) mod N
There must be some way to simplify it by considering it as
(a - b - b + 1) mod N ?
EDIT:
I have stumbled upon the following property too:
ab mod N = ((a mod N) (b mod N)) mod N
Will this be helpful somehow?
If: (a+b) mod N = ((a mod N) + (b mod N)) mod N
then:
(a - 2*b + 1) mod N = ((a mod N) - (b mod N) - (b mod N) + (1 mod N)) mod N
It is simpler with large values of a and b and a small value for N.
For example: a=85773, b = 77733340, N=5:
which would you rather solve
(85773 - 77733340 - 77733340 + 1) mod 5
or
((85773 mod 5) - (77733340 mod 5) - (77733340 mod 5) + (1 mod 5)) mod 5
for the second one i get (3 - 0 - 0 + 1) % 5 = 4
There is no way to simplify (b*-2 + a + 1) % n unfortunately.

Resources