Hey all: I thought I might share a little thing I've been hacking on called "symbolic functions", since Henry Baker recently told math-fun about chebfun (IIRC). First, define a pythag/distance function because we'll need it later:
(defun pythag (x y) (sqrt (+ (* x x) (* y y))))
PYTHAG Now we define a symbolic function UNIT-SINE:
(define-symbolic-function unit-sine (x) (sin (* (* 2 pi) x)))
UNIT-SINE Of course this is thew sine function with a period of 1. We can evaluate it as normal:
(unit-sine 1.0)
-2.4492935982947064d-16 ;; approximately 0.0 It can be used in every way a traditional Lisp function can be used. Mapped, reduced, composed, disposed, etc. But it has a special property. We can do cool operations with it. For example, we can differentiate it:
(pythag (unit-sine 0.75) (funcall (differentiate #'unit-sine) 0.75))
1.0d0 which is to be expected from sin^2 + cos^2 = 1. But of course, maybe I'm just using the difference quotient. But:
(loop :for i :from 0.0 :to 1.0 :by 0.1 :collect (- (* 2 pi (cos (* 2 pi i))) (funcall (differentiate #'unit-sine) i)))
(0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0) Surely with something like the difference quotient, we'd get some numerical error. Of course, there's a trick, and it's what I call "symbolic functions". They're likely not a new idea at all, but I've never seen them like this. A symbolic function is a regular ol' executable function equipped with its symbolic form under the hood. It has the appearance of a regular function and can be used in every way a regular function can be used, so it's a transparent thing. But evaluating one by itself exposes the information:
#'unit-sine
#<X -> (SIN (* (* 2 PI) X))> Like I said, this is a regular, *COMPILED* function. When you provide a value for X, it is not re-interpreted to get a numerical value. It's calling the compiled form of that. However, numerical evaluation isn't the interesting thing. We can also produce new kinds of function using SLAMBDA (Symbolic LAMBDA):
(slambda (x) (* x x))
#<X -> (* X X)> which work exactly like LAMBDA functions. What happens if we differentiate?
(differentiate (slambda (x) (* x x)))
#<X -> (+ (* 1 X) (* X 1))> We get a brand new SLAMBDA, recompiled on-the-fly (so a poor man's JIT!). DIFFERENTIATE is really just a classical (and very naive) symbolic differentiator: https://bitbucket.org/tarballs_are_good/symbolic-function/raw/fa394a19d24e/d... Also notice that DIFFERENTIATE never required any "variable" parameter. Since differentiation occurs on *functions* and not *expressions*, we know what we are differentiating with respect to. (This is in my opinion the right way to differentiate.) What does differentiating UNIT-SINE produce?
(differentiate #'unit-sine) #<X -> (* (+ (* (+ (* 0 PI) (* 2 0)) X) (* (* 2 PI) 1)) (COS (* (* 2 PI) X)))>
Of course, that's very ugly, but when compiled, that ugliness gets washed away through constant folding and friends. However (!), it would not be difficult to write a simple symbolic simplifier for symbolic functions either. Anyway, the code is here: Main Meat: https://bitbucket.org/tarballs_are_good/symbolic-function/raw/fa394a19d24e/s... Repository: https://bitbucket.org/tarballs_are_good/symbolic-function There are some outstanding "problems" really. For example, it's not immediately clear to me how to symbolically manipulate constructs like LET and friends in a mathematical fashion, or deal with symbolic functions that stray away from "symbolicness" (like functions which use mutation, etc.) But I think the biggest advantage is that you get truly fast numerical code which is transparently done with symbolic operations, sort of like chebfun (except chebfun is more numeric than symbolic: it just has function approximations). If you're interested in running the code, you need a dependency (the metaobject protocol), and you need to know how to use ASDF (or Quicklisp). (I'd be more than happy to describe those steps.) Any comments, history, suggestions, fan fiction, love letters, death threats? Cheers, Robert Smith