# Parallel Mandelbrot Roots
A task when exploring the Mandelbrot set is to find roots such that for some target . When , will be a nucleus of a hyperbolic component. The case has applications like tracing external rays in and out, or tracing equipotentials.
The serial algorithm is obvious but takes time.
What if it were possible to wait less time (at the potential cost of more resource consumption) by parallelizing the implementation?
# 1 Serial Algorithm
Newton’s method in one complex variable. Iterate and , and update
space, work, time, processors.
# 2 Matrix Algorithm
Consider the orbit of from iteration to iteration as a vector. Then let for and and solve for the vector using Newton’s method in variables. This involves solving the matrix equation and updating
The matrix has a regular form: it is upper bidiagonal with an added initial column of values:
We can use a trick based on Sherman-Morrison formula to turn a complicated solve into two simpler solves. As so can be solved by solving
The matrix is upper bidiagonal, so can be solved by back-substitution in in space, work, time on processors.
# 3 Parallel Algorithm
The matrix algorithm can be parallelized to run in in space, work, time on processors using work-efficient parallel prefix scan.
However, the work done (while still ) is a significant constant factor more than the serial algorithm, and the space requirements make it infeasible for large .
# 4 Benchmark
Task: given the Millionaires #001 deep zoom Mandelbrot set location expressed as a sequence of offsets and periods, find each root along the way to the final view:
1 0.0000000000000000e+00 0.0000000000000000e+00
2 -1.0000000000000000e+00 0.0000000000000000e+00
3 -7.5487766624669272e-01 0.0000000000000000e+00
4 -1.8592214028279200e-01 0.0000000000000000e+00
8 -9.8228378871340798e-04 0.0000000000000000e+00
69 2.1724309435420009e-04 2.3489118850327509e-04
142 1.3225292985888009e-11 7.1358633969614633e-12
272 2.8529310483870992e-16 1.0267761000262062e-15
560 -1.6383387496504541e-22 2.0300116425226282e-22
1104 -2.1353430107983675e-33 7.6284717085111253e-34
2228 1.5911948904690584e-49 4.0112501481921464e-49
4436 2.3215027144370148e-74 1.3325698720132981e-73
8896 -5.9289281961504007e-110 9.1490695754906453e-110
17768 -1.6130749406127237e-164 3.3282600257703784e-165
35564 7.5630380778794724e-247 2.4419891481973179e-246
71100 -2.2206801493397202e-370 1.8281359915497428e-369
142232 -4.3057238712082329e-554 3.0489078189532703e-554
284432 4.8325042482703457e-831 2.4686705119967285e-831
568900 -1.1303349232678663e-1246 -9.4216823889086909e-1247
1137764 -6.2022423569065681e-1870 5.0076670707505569e-1870
5.0e-2805
The final line is the size of the view; the precision required for each root is based on the magnitude of the next line’s offset.
The serial algorithm is trivial to implement, and took 5 minutes at 99% CPU with 4MB peak RAM usage.
The parallel algorithm is hard to implement, and took 6min40s at 1548% CPU with 17GB peak RAM usage.
Conclusion: the serial algorithm is better in all aspects, at least on a CPU with modest parallelism (16 threads). On a GPU the story might be different. Work in progress…