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.
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:
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.
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.
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)