Common Lisp - symbolic polynomial calculation - common-lisp

I would like to perform some symbolic calculations on lisp.
I found useful derivative function and I would like to know how to write simple recursive function to add/substract/etc. polynomials.
Input (e.g.): (addpolynomial '(+ (^ (* 2 x) 5) 3) '(+ (^ (* 3 x) 5) (^ (* 3 x) 2)))
Output: (+ (^ (* 5 x) 5) (^ (* 3 x) 2)) 3)
Do you know how to do this?
Or maybe you know other symbolic calculation examples?

When I've dealt with polynomials in Lisp in the past, I've used arrays of numbers (letting the variable be assumed, which means I couldn't trivially have things like "x*x + y", but since I didn't need that...).
That allows you to represent "2x^5 + 3" as #(3 0 0 0 0 2), finding the factor of x^n by (aref poly n) and other handy operations.
This also allows you to define addition as simply (map 'vector #'+ ...) (multiplication requires a bit more work).

Related

Rewriting sine using simprocs in Isabelle

I want to implement a simproc capable of rewriting the argument of sin into a linear combination x + k * pi + k' * pi / 2 (where ideally k' = 0 or k' = 1) and then apply existing lemmas about additions of arguments in sines.
The steps could be as follows:
Pattern match the goal to extract the argument of sin(expr):
fun dest_sine t =
case t of
(#{term "(sin):: real ⇒ real"} $ t') => t'
| _ => raise TERM ("dest_sine", [t]) ;
Prove that for some x, k, k': expr = x + k*pi + k' * pi/2.
Use existing lemmas to rewrite to a simpler trigonometric function:
fun rewriter x k k' =
if (k mod 2 = 0 andalso k' = 0) then #{term "sin"} $ x
else if (k mod 2 = 0 andalso k' = 1) then #{term "cos"} $ x
else if (k mod 2 = 1 andalso k' = 0) then #{term "-sin"} $ x
else #{term "-cos"} $ x
I'm stuck at step two. The idea is to use algebra simplifications to obtain the x,k,k' where the theorem holds. I believe schematic goals should do this but I haven't ever used them.
My thoughts
Could I rather assume that the expression is of this form and let the simplifier find it so that the simproc can be triggered?
If I first start assuming the linear form x + k*pi + k' * pi/2 then:
Extract x,k,k' from this combination.
Apply rewriter and obtain the corresponding term to be rewritten two.
Apply in a sequence: rules dealing with + pi/2, rules dealing with + 2 pi
I would start easy and ignore the pi / 2 part for now.
You probably want to build a simproc that matches on anything of the form sin x. Then you want to write a conversion that takes that term x (which is assumed to be a sum of several terms) and brings it into the form a + of_int b * p.
A conversion is essentially a function of type cterm → thm which takes a cterm ct and returns a theorem of the form ct ≡ …, i.e. it's a form of deterministic rewriting (a conversion can also fail by throwing a CTERM exception, by convention). There are a lot of combinators for building and using these in Pure/conv.ML.
This is probably a bit fiddly. You essentially have to descend through the term and, for each atom (i.e. anything not of the form _ + _) you have to figure out whether it can be brought into the form of_int … * pi (e.g. again by writing a conversion that does this transformation – to make it easy you can omit this part so that your procedure only works if the terms are already in that form) and then group all the terms of the form of_int … * pi to the right and all the terms not of that form to the left using associativity and commutativity.
I would suggest this:
Define a function SIN_SIMPROC_ATOM x n = x + of_int n * pi
Write a conversion sin_atom_conv that rewrites of_int n * pi to SIN_SIMPROC_ATOM 0 n and everything else into SIN_SIMPROC_ATOM x 0
Write a conversion that descends through +, applies sin_atom_conv to every atom, and then applies some kind of combination rule like SIN_SIMPROC_ATOM x1 n1 + SIN_SIMPROC_ATOM x2 n2 = SIN_SIMPROC_ATOM (x1 + x2) (n1 + n2)
In the end, you have rewritten your entire form to the form sin (SIN_SIMPROC_ATOM x n), and then you can apply some suitable rule to that.
It's not quite clear to me how to best handle the parity of n. You could rewrite sin (SIN_SIMPROC_ATOM x n) = (-1) ^ nat ¦n¦ * sin x but I'm not sure if that's what the user really wants in most cases. It might make more sense to only do that if you can deduce the parity of n statically (e.g. by using the simplifier) and then directly simplify to sin x or -sin x.
The situation becomes even more complicated if you want to include halves of π. You can of course extend SIN_SIMPROC_ATOM by a second term for halves of π (and one for doubles of π as well to make it more uniform). Or you could ad all of them together so that you just have a single integer n that describes your multiples of π/2, and k multiples of π simply contribute 2k to that term. And then you have to figure out what n mod 4 is – possibly again with the simplifier or with some clever static method.

Function returns 0 when it should return 1, eliminating parantheses

Consider the following minimal working example of Isabelle, where I defined two different functions, func1 and func2, that should emulate Eulers Totient function.
Oddly, the obivious definition is false and changing the definition only slightly by introducing ∈ℕ leads to correct, but yet unprovable definition.
(The exact questions I interspersed with the code, as that makes it probably clearer to what I'm referring).
theory T
imports
Complex_Main
"~~/src/HOL/Number_Theory/Number_Theory"
begin
(* Part I*)
definition func1 :: "nat ⇒ nat"
where "func1 n = card {m. m≤n ∧ coprime n m}"
lemma func1_equals : "func1 1 = 2" (* This equation is obviously false...*)
by (auto simp: func1_def)
(* Question 1: Why is this proof correct according to Isabelle? *)
(* Part II*)
definition func2 :: "nat ⇒ nat"
where "func2 n = card {m. m∈ℕ ∧ m≤n ∧(coprime n m)}"
(* Slightly changed definition by incorporating ∈ℕ*)
lemma func2_equals : "func2 1 = 1"
apply (auto simp: func2_def)
(* Unproved subgoal <<card {m ∈ ℕ. m ≤ Suc 0} = Suc >> looks more promising *)
oops
(* Question 2: Which proof method should I use to prove the last lemma?
Interestingly, sledgehammer runs out of time...*)
Question 3: Analogous to Q2 but for func2 4 = 2 ? The difference now is that
the preliminary <<apply (auto simp: func2_def)>> rewrites to a slightly
different subgoal. *)
end
Are there perhaps any more elegant ways to define the Euler totient function ?
Your two definitions are equivalent:
lemma "func1 n = func2 n"
by (simp add: func1_def func2_def Nats_def)
The reason why you get 2 instead of 1 is a subtle one and a perfect example of the kind of problems you run into when you formalise mathematical definitions: The natural numbers in Isabelle contain 0, so if you evaluate func1 1, you look at the numbers no greater than 1 – 0 and 1 – and check which of them are coprime to 1. Since gcd 0 n = n for all n, you find that both 0 and 1 are coprime to 1 and therefore the result is 2.
Euler's totient function only counts the positive integers less than n that are coprime to n, so your definition should look more like this:
definition totient :: "nat ⇒ nat" where
"totient n = card {k ∈ {0<..n}. coprime k n}"
If you want code generation to work, you can use the following code equation:
lemma totient_code [code]: "totient n = card (Set.filter (λk. coprime k n) {0<..n})"
by (simp add: totient_def Set.filter_def)
Then you can do this:
value "map (int ∘ totient) [1..<10]"
(* Output: "[1, 1, 2, 2, 4, 2, 6, 4, 6]" :: "int list" *)
Note that while I think the totient function would be very nice to have in Isabelle, I'm not sure it's the best think for a beginner to explore, since the proofs involving cardinalities of sets and these things might get a bit involved. A good way to get to know the system is the free bok Concrete Semantics by Nipkow and Klein. The examples and exercises in that book are more computer science/programming-oriented, but the material is more geared towards beginners and more amenable to interactive theorem proving in the sense that the proofs tend to get less messy and require less experience with the system.
And, by the way, the reason why sledgehammer fails to prove func2 1 = 1 is because the result is 2 and not 1. You can prove that by unfolding the definition of ℕ (which, for natural numbers, is basically just UNIV) as I did above, either by doing unfolding Nats_def or adding Nats_def to the simp set with simp add:.

Solving "n-rooks" with tail recursion

Im trying to solve the n rooks problem with tail recursion since it is faster than standard recursion, but am having trouble figuring our how to make it all work. I've looked up the theory behind this problem and found that the solution is given by something called "telephone numbers," which are given by the equation:
where T(1) = 1 and T(2) = 2.
I have created a recursive function that evaluates this equation but it only works quickly up to T(40), and I need it to calculate where n > 1000, which currently by my estimates will take days of computation.
Tail recursion seems to be my best option, but I was hoping someone here might know how to program this relation using tail recursion, as I don't really understand it.
I'm working in LISP, but would be open to using any language that supports tail recursion
Tail recursion is just a loop expressed as recursion.
When you have a recursive definition that "forks", the naive implementation is most likely exponential in time, going from T(n) to T(n-1) and T(n-2), then T(n-2), T(n-3), T(n-3), T(n-4), doubling the computation at each step.
The trick is reversing the computation so that you build up from T(1) and T(2). This just needs constant time at each step so the overall computation is linear.
Start with
(let ((n 2)
(t-n 2)
(t-n-1 1))
…)
Let's use a do loop for updating:
(do ((n 2 (1+ n))
(t-n 2 (+ t-n (* n t-n-1)))
(t-n-1 1 t-n))
…)
Now you just need to stop when you reach your desired n:
(defun telephone-number (x)
(do ((n 2 (1+ n))
(t-n 2 (+ t-n (* n t-n-1)))
(t-n-1 1 t-n))
((= n x) t-n)))
To be complete, check your inputs:
(defun telephone-number (x)
(check-type x (integer 1))
(if (< x 3)
x
(do ((n 2 (1+ n))
(t-n 2 (+ t-n (* n t-n-1)))
(t-n-1 1 t-n))
((= n x) t-n))))
Also, do write tests and add documentation what this is for and how to use it. This is untested yet.
When writing this tail recursive, you recurse with the new values:
(defun telephone (x)
(labels ((tel-aux (n t-n t-n-1)
(if (= n x)
t-n
(tel-aux (1+ n)
(+ t-n (* n t-n-1))
t-n))))
(tel-aux 2 2 1)))
When tail recursion is optimized, this scales like the loop (but the constant factor might differ). Note that Common Lisp does not mandate tail call optimization.

What is the difference between these two recursive functions?

The problem is to find the nth power of x^n of a number x, where n i s a positive integer. What is the difference between the two pieces of code below. They both produce the same result.
This is the code for the first one:
(define (power x n)
(define (square n) (* n n))
(cond ((= n 1) x)
((even? n)
(square (power x (/ n 2))))
(else
(* (power x (- n 1)) x))))
This is the second one:
(define (power x n)
(if (= n 1)
x
(* x (power (- n 1) x))))
The difference is in the time it takes for the two algorithms to run.
The second one is the simpler, but also less efficient: it requires O(n) multiplications to calculate x^n.
The first one is called the square-and-multiply algorithm. Essentially, it uses the binary representation of the exponent, and uses the identities
x^(ab) = ((x^a)^b)
x^(a+b) = (x^a)(x^b)
to calculate the result. It needs only O(log n) multiplications to calculate the result.
Wikipedia has some detailed analysis of this.

Creating a tail-recursive power function in scheme

I was wondering how do you implement a tail-resursive power function in scheme?
I've got my recursive one defined for scheme:
(define power-is-fun
(lambda (x y)
(cond [(= y 0)
1]
[(> y 0)
(* (power-is-fun x (- y 1)) x)])))
But I can't quite figure out how the other one is supposed to be.
The answer is similar, you just have to pass the accumulated result as a parameter:
(define power-is-fun
(lambda (x y acc)
(cond [(= y 0)
acc]
[(> y 0)
(power-is-fun x (- y 1) (* x acc))])))
Call it like this, notice that the initial value for acc is 1 (you can build a helper function for this, so you don't have to remember to pass the 1 every time):
(power-is-fun 2 3 1)
> 8
Some general pointers to transform a recursive procedure to a tail-recursion:
Add an extra parameter to the function to hold the result accumulated so far
Pass the initial value for the accumulator the first time you call the procedure, typically this is the same value that you'd have returned at the base case in a "normal" (non-tail-recursive) recursion.
Return the accumulator at the base case of the recursion
At the recursive step, update the accumulated result with a new value and pass it to the recursive call
And the most important: when the time comes to call the recursion, make sure to call it as the last expression with no "additional work" to be performed. For example, in your original code you performed a multiplication after calling power-is-fun, whereas in the tail-recursive version, the call to power-is-fun is the last thing that happens before exiting the procedure
By the way, there is a faster way to implement integer exponentiation. Rather than multiplying x over and over again, y times (which makes it O(y)), there is an approach that is O(log y) in time complexity:
(define (integer-expt x y)
(do ((x x (* x x))
(y y (quotient y 2))
(r 1 (if (odd? y) (* r x) r)))
((zero? y) r)))
If you dislike do (as many Schemers I know do), here's a version that tail-recurses explicitly (you can also write it with named let too, of course):
(define (integer-expt x y)
(define (inner x y r)
(if (zero? y) r
(inner (* x x)
(quotient y 2)
(if (odd? y) (* r x) r))))
(inner x y 1))
(Any decent Scheme implementation should macro-expand both versions to exactly the same code, by the way. Also, just like Óscar's solution, I use an accumulator, only here I call it r (for "result").)
Óscar's list of advice is excellent.
If you like a more in-depth treatment of iterative (aka linear recursive) and tree recursion processes, then don't miss out on the great treatment in SICP:
http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-11.html#%_sec_1.2

Resources