Fractal Equations Overview
Each program is composed of a set of properties and instructions.
Always remember to click the Toggle Code View toolbar button at the top of the Program Editor and read the comments given in the comment section of the program's instructions. The comment section contains usage instructions, hints, notes, documentation, and other important information that will help you understand how best to use the program. Once you are done reading the comments, click the Toggle Code View toolbar button again to view the program's properties.
The following properties are supported:
The remainder of this page can be ignored if you are not a programmer.
The instructions are divided into sections. Within each section are statements that conform to the Programming Language syntax.
In addition to the Standard Sections, Fractal Equations support 2 other sections:
The initialize section is executed once per pixel, just prior to beginning the orbit. Instructions that need to execute at the beginning of each orbit should be placed here. The iterate section is executed once per iteration and is the primary section of the fractal equation.
These instructions can be used to produce a Mandelbrot fractal or any of the related Julia fractals. A separate set of instructions is not required for Julia fractals. The choice of Mandelbrot or Julia is made simply by checking the Julia checkbox and setting the Julia Constant. The Fractal Science Kit will initialize z and c to the appropriate values based on these settings prior to invoking your instructions. Even checking the Julia checkbox and setting the Julia Constant is handled for you if you use the Preview Julia feature and simply click on an existing Mandelbrot display to select the Julia Constant.
There is one situation where your code needs to be aware of which type (Mandelbrot or Julia) of fractal it is producing. If the instructions set the initial value of z in the initialize section of the instructions, you need to protect this code when you are producing a Julia fractal.
In this example, the instructions provide an option K to modify the fractal equation. Since this is the value we want to use for the initial value of z in the case of Mandelbrot fractals, we need to assign K to z in the initialize section. The problem is that for Julia fractals, z should be initialized to the pixel value not K. We need to make sure we do not change z if we are producing a Julia fractal, hence the code:
This checks the built-in Boolean constant IsJulia and initializes z only if IsJulia is false. IsJulia is true if the Julia checkbox is checked and false otherwise. The only time you need to worry about whether the equation is being used to produce a Mandelbrot fractal or a Julia fractal is when the initial z value is not a literal number. Otherwise, you can simply set the Initial Z property to the appropriate value and let the Fractal Science Kit handle the details for you.
To clarify, just before executing the initialize section, the Fractal Science Kit does the following:
First, the pixel is transformed by the current Transformation. Then the variables c and z are initialized based on the type of fractal we are producing. Julia fractals assign the Julia Constant to c and the (transformed) pixel value to z. Mandelbrot fractals assign the (transformed) pixel value to c and the Initial Z value to z. If you need to initialize z in the initialize section of the instructions, you need to check the IsJulia constant and only execute the assignment if IsJulia is false indicating you are producing a Mandelbrot fractal. Otherwise, the Julia fractal will not display properly.
The pseudo-code given here assigns a value to pixel and to c which is not possible in your code because both are read-only variables which cannot be changed; in truth, for Julia fractals, c is a constant and is initialized during compilation. However, the point is that if you need to initialize z to a value computed at runtime, you need to protect Julia fractals from that change using the IsJulia constant as shown previously. You also have access to the constants JuliaConstant and InitialZ, and the read-only variable pixel but these are rarely used.
Be forewarned, this section requires a basic understanding of calculus. Specifically, you need to understand the concept of the derivative of a function. If this concept is not familiar to you, you can simply skip this section.
In the previous discussion, you may have wondered how one decides what value should be used for the initial value of z. It turns out that the best choice for the initial value of z is a critical point of the fractal equation. A critical point is defined as a value that satisfies the equation: f'(z)=0, where f'(z) is the 1st derivative of the fractal equation. That is, we take the derivative of the fractal equation, set it to 0, and solve for z. Whether this is easy, difficult, or even possible, depends on the fractal equation.
For example, the classic Mandelbrot equation is:
f(z) = z^2 + c
The derivative of f(z) is:
f'(z) = 2*z
Setting f'(z)=0 and solving for z yields:
z = 0
So the critical point is 0 for this formula. However, the 2nd example given above, used the equation:
f(z) = z^3 - 3*K^2*z + c
The derivative of f(z) is:
f'(z) = 3*z^2 - 3*K^2
Setting f'(z)=0 and solving for z yields:
z = K or -K
So the 2 critical points are +/-K for this formula. Since K is squared in the original equation, both critical points yield the same results. This is why we set z=K in the initialize section of the program. Other values can be used for the Initial Z and sometimes the results are interesting but using a critical point is always a good thing to try if you can. Otherwise, just try 0 or 1 or any other number!
This example is identical to the previous example but applies Horner's scheme to evaluate the polynomial function defined by the coefficients in the coef array at the value z. The number of elements in the coef array defines the degree of the polynomial (e.g., a 3rd degree polynomial like the one above has 4 values) and that any terms missing in the equation must have a 0 coefficient. In this example, the term for x^2 is missing in the equation so the 2nd element of the coef array is set to 0.
Notice how we set coef=c if we are processing a Mandelbrot fractal. This is required for Mandelbrot fractals since c is initialized to the pixel location just prior to calling the initialize section so the assignment in the global section is not correct with respect to coef. This is not required for Julia fractals since c is set to the Julia Constant before the program runs and is never changed. We could move the initialization of the coef array out of the global section and into the initialize section to avoid this complexity and thereby simplify the code but this would be less efficient since the initialize section is executed at the beginning of every iteration while the global section is executed once during program compilation. In fact, since IsJulia is a constant, the compiler will evaluate the if statement during Program Optimization and remove the entire if block for Julia fractals, and at runtime, the initialize section is not even executed!
Horner.EvaluatePolynomial is highly optimized. See Horner Functions for details.
The previous examples were Divergent fractal equations. Newton fractals are examples of Convergent fractal equations.
To create a Newton fractal for a given equation f(z)=0, you set up a fractal equation based on Newton's method for finding the roots of an equation; i.e., z = z - f(z)/f'(z). In the above example:
f(z) = z^3 - c
So the fractal equation becomes:
z = z - (z^3-c)/(3*z^2)
To find the critical point, we observe that the derivative of g(z) = z - f(z)/f'(z) is:
g'(z) = 1 - (f'(z)^2 - f(z)*f''(z)) / f'(z)^2
So g'(z) is 0 for any value of z where f(z)=0 or f''(z)=0 but f'(z)<>0. However, f(z) cannot equal 0 or we terminate the iteration immediately based on the convergence criteria. So the critical point can be found by setting f''(z), the 2nd derivative of f(z), to 0 and solving for z but disallowing any value of z where f(z) or f'(z) is 0.
In the above example, the 2nd derivative of f(z) is:
f''(z) = 6*z
Setting f''(z)=0 and solving for z yields:
z = 0
Unfortunately, f'(0)=0 as well so 0 is disallowed so we arbitrarily choose 1 as the Initial Z value! Other values for the Initial Z work equally well (e.g., 0.5, 0.1, etc.) and produce different visual results.
In this case:
f(z) = z^3 + (c-1)*z - c
Setting f''(z)=0 and solving for z yields z=0 here as well but since f'(0)<>0 we can use 0 as the Initial Z.
If you are implementing a Newton fractal equation for a simple polynomial function like that given above, you can let the Fractal Science Kit calculate the fractal equation for you by using the Solver.ApplyToPolynomial function.
This example uses Solver.ApplyToPolynomial to implement the same equation as above. This method is highly optimized and is much faster than hand coding the equation. Notice how we set coef and coef if we are processing a Mandelbrot fractal. This is required since c is initialized to the pixel location just prior to calling the initialize section for Mandelbrot fractals but this is not required for Julia fractals since c is set to the Julia Constant before the program runs and is never changed.
Of course, in addition to Newton's method of finding the roots of an equation, there are many different root-finding methods including: Schroder's method, Halley's method, Whittaker's method, Cauchy's method, the Super-Newton method (also called Householder's method), the Super-Halley method, the Chebyshev-Halley family of methods, and the C-Iterative family of methods. Fractals associated with each of these methods are supported by Solver.ApplyToPolynomial too. The statement #include SolverMethodOptions in the properties section in the example above, adds 2 options to the associated properties page: SolverMethod and SolverMethodArg. These are passed to Solver.ApplyToPolynomial to allow you to set the root-finding method and the argument used by the family settings, respectively.
See Solver Functions for a detailed description of Solver.ApplyToPolynomial and the SolverMethodOptions.
When the function used as the basis for the root-finding method fractal is not a simple polynomial function, you need to use the less efficient but more flexible Solver.Apply function.
This example uses Solver.Apply to implement the fractal equation. Like Solver.ApplyToPolynomial, the 1st and 2nd arguments are user specified options that define the method and argument for the fractal. However, Solver.Apply also requires you to evaluate the function and the function's 1st and 2nd derivative at the current point z and pass these 3 values as the last 3 arguments as illustrated above.
See Solver Functions for a detailed description of Solver.Apply and the SolverMethodOptions.
The FSK.OverrideValue method can be used to override selected parameter settings. See FSK Functions for details.
Several built-in variables are available to your instructions:
The variable z is used to return the results computed by the program. All of the remaining variables are read-only.
The variables z and c have already been discussed. dwell is the current dwell value. The 1st time the iterate section is called, dwell=0, the 2nd time, dwell=1, and so on. pixel is the point on the complex plane associated with the orbit. zprev1 and zprev2 are the previous 2 values of z in the current orbit.
This example is an implementation of the Phoenix fractal and illustrates using zprev1 in the equation.
In addition to zprev1 and zprev2, you can access the entire set of orbit points computed thus far using the Orbit Functions.
The following constants are available to your instructions:
As described above, IsJulia equals True if the fractal is a Julia fractal and False otherwise. JuliaConstant is the value of the Julia Constant property and InitialZ is the value of the Initial Z property. With the exception of IsJulia, these constants are rarely used.
Copyright © 2004-2019 Ross Hilbert