This is part of an exchange with math profs over on math-teach*, but I think is of relevance here, given all the Python. KU * http://www.mathforum.com/epigone/math-teach/ghyrturcrerl ==============

On Dec 26, 2004, at 4:01 PM, Kirby Urner wrote:

To recapitulate how we'll do some of the early calculus stuff using Python, the advantage of top-level functions is you can pass them as arguments to other functions. This mirrors the behavior of the derivative operator, which Spivak likes to symbolize as D. D(f) -> g, means you take a function, f, operate on it (D), and spit back another function, g.

Kirby,

You may want to check out so-called "automatic differentiation". The underlying idea is this: We overload the symbols of ordinary algebra, thus extending algebra to the collection of ordered pairs (u, u'), where u is a function and u' its derivative:

(u, u') + (v, v') := (u + v, u' + v');

(u, u') * (v, v') := (u * v, u' * v + u * v'),

etc.

We embed the reals in this system: the real number a --> (a, 0).

The identity function (which we abusively write as "x") is embedded as x --> (x, 1).

Of course, this can all be hidden from the user if we wish.

Let's suppose that we have told our computer how to extend addition and multiplication as above, and that we've given it the image of the identity function too.

Note what happens. When the machine uses these definitions to calculate the squaring function, we get

(x, 1) * (x, 1) = (x * x, 1 * x + x * 1) = (x^2, 2 x).

This happens for other things as well, so we don't need to give most of the formal embeddings. With what we've already done, we're ready to do polynomial calculus now. With a few more rules [sin x --> (sin x, cosx), the quotient rule

(u, u')/(v, v') = (u/v, (u' * v - u * v')/v^2),

etc.] we can do all of elementary calculus.

Thanks Lou. A first take in Python: write a Pair class to store (u, du) and define the basic four operations, as you've defined them; write a Function class to define the same four ops with respect to generic functions. Even ordinary numbers will be considered functions which "return their own value." class Pair: """ A generic pair (f, f'), both Functions (below), and rules for the four basic ops w/r to Pairs """ def __init__(self, u, du): self.u = u self.du = du def __add__(self, other): return Pair(self.u + other.u, self.du + other.du) def __sub__(self, other): return Pair(self.u - other.u, self.du - other.du) def __mul__(self, other): return Pair(self.u * other.u, \ self.du * other.u + self.u * other.du) def __div__(self, other): return Pair(self.u/other.u, \ (self.du * other.u - self.u * other.du)/other.u**2) class Function: """ A generic wrapper for uni-variable functions, allowing them to be composed using the four operations """ def __init__(self, f): self.f = lambda x: f(x) def __add__(self, other): return Function(lambda x: self.f(x) + other.f(x)) def __sub__(self,other): return Function(lambda x: self.f(x) - other.f(x)) def __mul__(self,other): return Function(lambda x: self.f(x) * other.f(x)) def __div__(self,other): return Function(lambda x: self.f(x) / other.f(x)) def __call__(self,x): """call wrapped function with argument x, return result""" return self.f(x) Now we define the identity function, and unity, per your language game.

def ident(x): return x

id = Function(ident) # Function-wrapped identity function

def one(x): return 1

unity = Function(one) # Function-wrapped one

Now we define a Pair (ident, unity) corresponding to your (x, 1).

p1 = Pair(id, unity)

Multiplying p1 * p1 should return a new pair such that newpair.u is a parabolic or 2nd powering function, and newpair.du is the derivative thereof.

newp = p1 * p1 [newp.u(i) for i in range(-5,6)] [25, 16, 9, 4, 1, 0, 1, 4, 9, 16, 25]

[newp.du(i) for i in range(-5,6)] [-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10]

So far so good. Thanks for this brain teaser and opportunity to show off functional programming in Python. Kirby