Mandelbrot Set Miscalculations

While developing a Java program to create an image of the Mandelbrot set, I stumbled upon a small error which completely changes the set’s look. To fix this bug, you need to swap two lines of code.

The bug arises when trying to convert convenient Python features to Java.
To iteratively apply the function z \to z^2 + c, you update your complex number z a certain amount of times. When you are not using a complex number class, but instead you use two floating point numbers (in Java doubles to gain precision) \texttt{a} and \texttt{b} to define the real and imaginary part (z = \texttt{a} + \texttt{b} \cdot i), logically both numbers need to be updated.
In Python you may write the following, when c is defined as being a complex number with parts \texttt{c} and \texttt{d} (c = \texttt{c} + \texttt{d}).

a, b = a**2 - b**2 + c, 2 * a * b + d

Which seems to be very similar to those two lines.

a = a**2 - b**2 + c
b = 2 * a * b + d

But notice that in the first code snippet you define a tuple consisting of the real and imaginary part and then assign it to the variables. The first snippet really looks like this.

t = (a**2 - b**2 + c, 2 * a * b + d)
a, b = t

Using this assignment of variables, which corresponds to z \to z^2 + c, you get an image of the correct Mandelbrot set.
mandel_correct
In contrary, the second code snippet assigns the old \texttt{a} its new value, then uses this new \texttt{a} to define the value of new \texttt{b}, thus does not calculate z \to z^2+c, which is equivalent to z \to (\texttt{a}^2-\texttt{b}^2+\texttt{c}) + (2\cdot\texttt{a}\cdot\texttt{b}+\texttt{d}) \cdot i, but rather z \to (\texttt{a}^2-\texttt{b}^2+\texttt{c}) + (2 \cdot \texttt{a}^2 \cdot \texttt{b}-2\cdot \texttt{b}^3+2\cdot\texttt{b}\cdot\texttt{c}+\texttt{d})\cdot i.

In Java it would look like this.

a = a*a - b*b + c;
b = 2 * a * b + d;

Which results in this rather unusual depiction of the famous fractal.
mandel_miscalculation
You can easily avoid this bug when using two sets of variables to define old z and new z, as shown in the following.

_a = a*a - b*b + c;
_b = 2 * a * b + d;
a = _a;
b = _b;

Or you can define variables \texttt{asqr} = \texttt{a}^2 and \texttt{bsqr} = \texttt{b}^2 and swap the assignment. Using variables for the squares of the parts of z also helps to improve performance.

b = 2 * a * b + d;
a = asqr - bsqr + c;
asqr = a*a;
bsqr = b*b;

// Java 1.8 Code
// Jonathan Frech, September / November / December 2016


// generates a 1080x720 zoomed out .png
// image of the Mandelbrot Set fractal

// import
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;

// main class
public class mandel {
	// main function
	public static void main(String[] args) {
		// dimensions
		int width  = 1080;
		int height = 720;
		
		// image
		BufferedImage img = new BufferedImage(
			width, height, BufferedImage.TYPE_INT_RGB
		);
		
		// p1: top left, p2: bottom right
		double p1y = -1.2;
		double p2y =  1.2;
		double p1x = -(p2y - p1y) / 2.0 * width / height - 0.5;
		double p2x =  (p2y - p1y) / 2.0 * width / height - 0.5;
		
		// transform pixel coordinates into complex numbers
		double facx = (p2x - p1x) / width;
		double facy = (p2y - p1y) / height;
		double addx = p1x;
		double addy = p1y;
		
		// variables needed
		double a, b, _a, _b, c, d, asqr, bsqr;
		int i;
		
		// go through image pixels
		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				
				// reset variables
				i = -1;
				a = 0;
				b = 0;
				asqr = 0;
				bsqr = 0;
				c = x * facx + addx;
				d = -(y * facy + addy);
				// iteratively apply f_c(z) = z*z + c
				while (asqr+bsqr <= 4) {
					// correct Mandelbrot set
					/*
					_a = a*a - b*b + c;
					_b = 2 * a * b + d;
					a = _a;
					b = _b;//*/
					
					// Mandelbrot set miscalculation
					//*
					a = a*a - b*b + c;
					b = 2 * a * b + d;//*/
					
					// re(z)**2, im(z)**2
					asqr = a*a;
					bsqr = b*b;
					
					// increment iterations needed
					i++;
					
					// too many iterations needed, abort
					if (i >= 255) {
						break;
					}
				}
				
				// set pixel color according to iterations needed
				int k = 255 - i;
				img.setRGB(x, y, ((k << 16) | (k << 8) | k));
			}
		}
		
		// save image to disk
		try {
            File file = new File(
            	System.getProperty("user.dir") + "/mandel.png"
            );
        	ImageIO.write(img, "png", file);
        }
        catch(IOException e) {
        	System.out.println("Error: " + e);
        }
	}
}
Advertisements

2 thoughts on “Mandelbrot Set Miscalculations

  1. Pingback: Mandelbrot Set II – J-Blog

  2. Pingback: Multibrot Set – J-Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s