Jump to content

Recommended Posts

Why are the containers relevant?  Let's take an example:

 

f'(x) where f(x) = sin(x^2)cos(x)

= (sin(x^2)cos(x))'

= sin'(x^2)cos(x) + sin(x^2)cos'(x)      (product rule)

= dsin(x^2)dx^2 * dx^2/dx * cos(x) - sin(x^2)sin(x)        (chain rule on left, cos' = -sin on right)

= 2xcos(x^2)cos(x) - sin(x^2)sin(x)    (sin' = cos, x^2' = 2x)

 

Nowhere do you need to consider the context of the derivative being found.  You only ever need to consider what is inside, and use the appropriate rule to push the derivative further down into the expression.

  • 2 years later...

I know this topic is three years old, but I was searching for something else and stumbled upon this and figured it was an interesting problem. Seeing as it's a really old topic, I've taken the liberty of not using PHP. Instead I'll use a functional programming language called Standard ML (SML).

 

Symbolic differentiation in its basic form is actually pretty easy to accomplish using a computer. The idea is to create an expression tree like roopurt said. We'll define a data structure with the type expr that defines the various types of elements our expression tree can contain:

 

datatype expr
  = Const of real 
  | X 
  | Prod of expr * expr
  | Quot of expr * expr
  | Exp of expr * int 
  | Sum of expr * expr
  | Diff of expr * expr
  | Ln of expr
  | Sin of expr
  | Cos of expr

 

The data structure is recursive, so a Sum contains two other exprs so we can create a tree structure, while Const (constants) and X (the variable) are possible leafs in our tree. So for now we support constants, one variable, products, quotients, exponentiation, sums, differences, the natural logarithm (ln) and the sin and cos functions.

 

To express 3-2x we would write Diff(Const 3.0, Prod(Const 2.0, X)). To define (3+x)/(2x) we would write Quot(Sum(Const 3.0, X), Prod(Const 2.0, X)). The first of these can be expressed using the following tree:

 

  -

/ \

3  *

  / \

  2  x

 

To actually compute the derivatives, we do it recursively down the tree. We start at the root and work down until we find a trivial case (a leaf).

 

In order to do this we need to define a set of rules for differentiation. Like "the derivative of a constant is 0", "the derivative of a sum is the sum of the derivatives of its operands" and so on. This can easily be expressed as recursive function:

 

fun D(Const _)   = Const 0.0 
  | D X          = Const 1.0 
  | D(Ln x)      = Quot(Const 1.0, x)
  | D(Sin x)     = Prod(Cos x, D x)
  | D(Cos x)     = Prod(Prod(Const ~1.0, Sin x), D x)
  | D(Sum(x,y))  = Sum(D x, D y)
  | D(Diff(x,y)) = Diff(D x, D y)
  | D(Prod(x,y)) = Sum(Prod(x, D y), Prod(D x, y)) 
  | D(Quot(x,y)) = Quot(Diff(Prod(D x, y), Prod(x, D y)), Prod(y, y)) 
  | D(Exp(x,y))  = Prod(Prod(Const (real y), Exp(x, y - 1)), D x)

 

Now, if we differentiate 3-2x using this D function we'll get Diff(Const 0.0, Sum(Prod(Const 2.0, Const 1.0), Prod(Const 0.0, X))) back, which is the equivalent of (0 - (2*1 + 0x)), which is correct, but not particularly pretty. Simply getting back -2 (or rather, Const ~2.0 (yes, SML uses ~ to mean negative)) would be much nicer.

 

Add more rules, and we are able to derive more types of functions.

 

Like we know a bunch of rules for differentiation, we also know some rules that can simplify an mathematical expression, like 0*a is just 0 for all a, and a/1 is just a. Again, we can apply this recursively on an expression. The problem with doing this, however, is that something that is not immediately simplifiable by our rules, may be able to be simplified when we've dealt with a subtree of the expression. This can be solved by simplifying until our simplification rules yield no change in the expression.

 

(* Does simplification of an expression *)
local
  (* Does one iteration of recursive simplification *)
  fun simplify'(Const x)  = Const x
    | simplify' X         = X 
    | simplify'(Sum z)    = (case z of
                                 (Const x, Const y) => Const (x + y)
                               | (Const 0.0,x)      => simplify' x
                               | (x,Const 0.0)      => simplify' x
                               | (x,y)              => Sum(simplify' x, simplify' y)) 
    | simplify'(Diff z)   = (case z of
                                  (x, Const 0.0)     => simplify' x
                                | (Const x, Const y) => Const(x - y)
                                | (x, y)             => Diff(simplify' x, simplify' y)) 
    | simplify'(Ln x)     = Ln(simplify' x)
    | simplify'(Cos x)    = Cos(simplify' x)
    | simplify'(Sin x)    = Sin(simplify' x)
    | simplify'(Prod z)   = (case z of
                                 (Const x, Const y) => Const (x * y)
                               | (Const 0.0, _)     => Const 0.0
                               | (_, Const 0.0)     => Const 0.0
                               | (Const 1.0, x)     => simplify' x
                               | (x, Const 1.0)     => simplify' x
                               | (x,y)              => Prod(simplify' x, simplify' y))
    | simplify'(Quot z)   = (case z of
                                 (x, Const 0.0)     => raise Div
                               | (Const x, Const y) => Const (x / y)
                               | (x, Const 1.0)     => simplify' x
                               | (x,y)              => Quot(simplify' x, simplify' y))
    | simplify'(Exp(x,1)) = x
    | simplify'(Exp(x,0)) = Const 1.0
    | simplify'(Exp(x,n)) = Exp(simplify' x, n)

  (* Simplifies until the expression doesn't change *)
  fun simplify'' cur old n = if cur = old andalso n > 0
                             then cur
                             else simplify'' (simplify' cur) cur (n + 1)
in
  (* Initializes the simplification loop *)
  fun simplify expr = simplify'' expr expr 0
end

 

Again we have a couple of base cases to end the recursion; it isn't possible to simplify constants or variables. The simplify function has two local auxiliary functions: simplify' (simplifies in one recursion) and simplify'' (simplifies until there is nothing more to do).

 

As with deriving, the more simplification rules we know, the better we can simplify complex mathematical expressions.

 

We can now perform symbolic differentiation in an SML prompt (- is the prompt and > is the result):

- val expr = Diff(Const 3.0, Prod(Const 2.0, X));
> val expr = Diff(Const 3.0, Prod(Const 2.0, X)) : expr
- D expr;
> val it = Diff(Const 0.0, Sum(Prod(Const 2.0, Const 1.0), Prod(Const 0.0, X)))
     : expr
- simplify it;
> val it = Const ~2.0 : expr

 

Evaluating an expression and turning it into a string is more or less the same thing: match which element you're working on, act accordingly and recurse if necessary. The finished script provides an eval function and a toStr function to accomplish this: http://files.degeberg.com/symdiff.sml

 

In the following we define f(x) = ln(sin(x)) and calculate f'(x) = cos(x)/sin(x) and use eval to calculate stuff with them:

- val expr = Ln(Sin X);
> val expr = Ln(Sin X) : expr
- val f = eval expr;
> val f = fn : real -> real
- val f' = eval (D expr);
> val f' = fn : real -> real
- f(~10.0);
> val it = ~0.608767226097 : real
- f'(~10.0);
> val it = ~1.54235104536 : real

 

We are obviously missing a pretty crucial part: parsing an expression. However, this is an entirely different concept.

 

All of the above concepts can be applied to PHP as well, but not nearly as elegantly. That's also why I chose to not write post with PHP examples as the number of lines would be several factors larger (plus I'm not working on solving anyone's problem).

 

Now that I'm at it, numerical differentiation is way easier and can be done in only one line:

fun d delta f x = (f (x + delta) - f (x - delta)) / (2.0 * delta)

 

The delta is simply a very small float.

Just thought i would put my 2 cents in.  Wolfram may be able to do what your talking about but i doubt a person without and indepth knowledge of derivatives will now whats going on.  You have standard derivatives +-*/.  Then you get equations that attempt to get a value close enough to the real derivative, taylor and maclaurine series.  Then you get laplase transforms that require you to change the values to something else then change them back again.

 

I have alot of notes on this on my website if you are keen h1vem1nd.org.  I got 93% for uni maths last semester and can assure you unless there is a magical inbuilt feature in php, this is going to be a nightmare for you.  Just deriving Sin(2x3) can be a mission if you dont know where to start.  So hit up the notes i have and see if they help.

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.