Mandelbrot with Plots.jl

I’m trying to do a crude sketch of the Mandelbrot set to better my understanding of Plots.jl. But the code is taking way too long to execute. Thus I humbly ask for help.
My class is as follows (the begin and end I use because I’m working on Pluto):

begin
	Base.@kwdef mutable struct frac
	z::Complex=0+0im
	c::Complex
	end
	
	function iterate!(f::frac)
		f.z=f.z^2+f.c
	end
end

And then I plot the graph by iterating through a discretized plane:

begin
	ploot=plot(1,xlim = (-2,2),ylim = (-2,2),title="mandelbrot iterator")
@gif for x=-2:1e-1:2,y=-1:1e-2:2
	mandel=frac(c=x+y*im)
	map(x->iterate!(mandel),(1:7))
	if(abs(mandel.z)<2)
		push!(ploot,x,y)
	end
end
end

This code has been running for at least an hour on my machine (6th gen i5+4GB GTX960 with tons of RAM). From my reading I’ve understood that my code isn’t leveraging the GPU, but I’m unable to think of a way to make Plots use the GPU.

Thank You

Probably the biggest issue with your code is that the struct frac has not concrete types - Complex is an abstract type, a concrete type would e.g. be Complex{Float64}.
Putting the main loop not into a function is another big performance killer (here Pluto does not save you by implicitly wrapping it into a function because the code contains a macro).

Here is a version of Mandelbrot calculation I did recently, hope this helps:

https://gist.github.com/lungben/07d342d0c7dfd4e655cccc1ecfa2922c

The notebook is not very nice (yet?), but the core functionality of calculating the Mandelbrot set should be correct and reasonably fast.

1 Like

Thanks I’ll check it out

I found the solution to be simply adding every 10 to my for loop:

begin
	ploot=plot(1,xlim = (-2,2),ylim = (-2,2),title="mandelbrot iterator")
@gif for x=-2:1e-2:2,y=-1:1e-2:2
	mandel=frac(c=x+y*im)
	map(x->iterate!(mandel),(1:10))
	if(abs(mandel.z)<2)
		push!(ploot,x,y)
	end
	end every 150
end

Not the best(in fact a good contender for the shittiest), but I’ll take it. If you came here looking for a good implementation, please look at @lungben’s reply.

1 Like

Btw. what made a large speed difference for me was replacing abs(z) < 2 by abs2(z) < 4.
Taking a square root is an expensive operation and not really required here.

4 Likes

Ahh yess, thank you again, when I wrote this code a week ago, that was on my todo list, and it completely slipped off.

For whatever it is worth, the following modified code uses GLMakie instead of Plots and avoids plotting/collecting the images of each iteration (there are >10,000 points to be plotted in original code snippet). The function mandelbrot1 returns an Nx2 array with the solutions (and NaNs elsewhere), which is plotted once instead. It runs almost instantaneously for your original parameters.

begin
	Base.@kwdef mutable struct frac
	   z::Complex{Float64} = 0 + 0im
       c::Complex{Float64}
	end
	
	function iterate!(f::frac)
		f.z = (f.z)^2 + f.c
	end
end

function manderlbrot1(x,y)
    n, m = size(x,1), size(y,1)
    mxy = fill(NaN, m*n,2)
    k = 1;
    for xk in x, yk in y
        mandel = frac(c = xk + yk*im)
        map(x -> iterate!(mandel), 1:7)
        if abs2(mandel.z) < 4
            mxy[k,:] = [xk, yk]
            k += 1
        end
    end
    return mxy
end

using GLMakie
x, y = -2:1e-1:2, -1:1e-2:2;
mxy = manderlbrot1(x,y)
scene = Scene()
lines!(scene, mxy[:,1], mxy[:,2], color = :blue, linewidth = 1)
Makie.save("mandelbrot_iterator.png", scene)

1 Like

It doesn’t work on my machine per se, but that’s probably due to installation faults on my end, which I shall try to fix

But I’m marking your answer as the solution because if anybody stumbles upon this topic, this is what they should see.