Taylor Series Fractals


The taylor series is a mechanism for approximating a function by summing polynomial terms.

First, some definitions:

f(x) the function to approximate
fn(x) the nth derivative of f (note that f0(x) = f(x))
n! n factorial, n! = n * (n - 1) * (n - 2) * ... * 2

The taylor series expansion of f(x) around a point (c) is then written:


        n -> infinity
        +----
         \      fn(c) * (x - c) n
  f(x) = +      -----------------
         /             n!
        +----
        n = 0
 

As an example, consider the case where:

f(x) => exp(x)
c => 0

Since the derivative of exp(x) is exp(x) we know that fn(x) = exp(x) for all n.

Here are the first few terms of the series:

value of n term value of term
0 exp(0) * (x - 0)0 / 0! 1
1 exp(0) * (x - 0)1 / 1! x
2 exp(0) * (x - 0)2 / 2! x2 / 2
3 exp(0) * (x - 0)3 / 3! x3 / 6
4 exp(0) * (x - 0)4 / 4! x4 / 24

We can write the expansion of exp(x) around c = 0 more concisely as follows:

        n -> infinity
        +----
         \      xn
  f(x) = +      --
         /      n!
        +----
        n = 0
 

Now lets look at how accurate the approximation is as we add more terms. In this case, lets use x = 3.

exp(3) n sum approximation difference
20.08553692318766774092 0 1 1 19.08553692318766774092
20.08553692318766774092 1 1 + x 4 16.08553692318766774092
20.08553692318766774092 2 1 + x + x2/2 8.5 11.58553692318766774092
20.08553692318766774092 3 1 + x + x2/2 + x3/6 13 7.08553692318766774092
20.08553692318766774092 4 1 + x + x2/2 + x3/6 + x4/24 16.375 3.71053692318766774092

As you can see, the approximation converges pretty rapidly on the actual value of exp(x).

Ok, so how do we apply this to generating a fractal? Firstly, all the statements about 'x' above apply equally well to 'z' where z is the complex number (x + iy).

In our case, assume 'current' is the point 'z' for which we wish to create an approximation of exp(z). Our algorithm (in pseudo code) looks like this:

 set actualValue   = exp(current)
 set maxIterations = 20
 set tolerance     = .00001
 set count         = 0
 set zSum          = [0, 0]
 set denominator   = 1

 while count is less than maxIterations and
       the magnitude of the difference between actualValue and zSum is
       greater than tolerance
 do
    zSum  = zSum + currentcount / denominator

    if count is greater than 0
       denominator = denominator * (count + 1)
    endif

    count = count + 1
 done
 

In this example, our expansion point (c) is zero, we can readily generalize our algorithm to handle other values of (c)

 set actualValue   = exp(current)
 set maxIterations = 20
 set tolerance     = .00001
 set count         = 0
 set zSum          = [0, 0]
 set denominator   = 1
 set c             = [2, 3]
 set cValue        = exp(c)

 while count is less than maxIterations and
       the magnitude of the difference between actualValue and zSum is
       greater than tolerance
 do
    zSum  = zSum + cValue * (current - c)count / denominator

    if count is greater than 0
       denominator = denominator * (count + 1)
    endif

    count = count + 1
 done
 

Finally, here is a full-on fractorama formula file demonstrating this technique

fractal
{
   mapping {
      (-2.33672316384180778215,
       3.41009999999999990905,
       1.18621468926553697010,
       8.00000000000000000000) => (150, 195)
   }


   formula
   {
      // The value we wish to approximate

      zvalue = exp(current);

      $xavg = 0;    
      $yavg = 0;    
      $mavg = 0;    

      $xmax = 0;    
      $ymax = 0;    
      $mmax = 0;    

      $xtot = 0;    
      $ytot = 0;    
      $mtot = 0;    
 
      $maxIterations = 15;
      $tolerance     = 1e-6;

      zSum           = 0;
      $denominator   = 1;

      // The point around which we'll create our expansion.  We use
      // exp(current) here but we could just as well have used:
      //
      // point = [2, 3]
      // point = sin(current)
      // etc.
      //
      // There is no special significance to the fact that we're using
      // exp(current) and we're attempting to approximate exp.

      point = exp(current);

      // Loop until we've been through the loop '$maxIterations' times
      // or we deem that zSum is close enough to zvalue

      while($count < $maxIterations && ssq(zvalue - zSum) > $tolerance)
      {
         zSum += exp(point) * ((current - point) ^ $count) / $denominator;

         if($count > 0) { $denominator *= ($count + 1); }

         value = cot(zSum);

         $x = mag(value ^ .1);
         $y = mag(value ^ .2);
         $m = mag(value ^ .4);

         $xavg = ($x + $xavg * $count) / ($count + 1);
         $yavg = ($y + $yavg * $count) / ($count + 1);
         $mavg = ($m + $mavg * $count) / ($count + 1);

         $xdev = sqrt(($x - $xavg) * ($x - $xavg));
         $ydev = sqrt(($y - $yavg) * ($y - $yavg));
         $mdev = sqrt(($m - $mavg) * ($m - $mavg));

         $xmax = max($xmax, $xdev);
         $ymax = max($ymax, $ydev);
         $mmax = max($mmax, $mdev);

         $xtot += $xdev;
         $ytot += $ydev;
         $mtot += $mdev;
      }


      $r = 0;
      $g = 0;
      $b = 0;

      if($count > 0)
      {
         $r = ($xtot/$count) / $xmax;
         $g = ($ytot/$count) / $ymax;
         $b = ($mtot/$count) / $mmax;

         $r = get_sin_color($r, 255, 1);
         $g = get_sin_color($g, 255, 1);
         $b = get_sin_color($b, 255, 1);
      }

      set_color($r, $g, $b);
   }
}

And here is the resulting image: