Mandelbrot set: Difference between revisions

→‎Normalized Counting, Distance Estimation, Mercator Maps and Perturbation Theory: Removed the slow calculation with BigFloats because perturbation theory is much better.
(→‎Normalized Counting, Distance Estimation, Mercator Maps and Perturbation Theory: Finally, some cuts and an example of individual deep zoom images.)
(→‎Normalized Counting, Distance Estimation, Mercator Maps and Perturbation Theory: Removed the slow calculation with BigFloats because perturbation theory is much better.)
Line 6,505:
savefig("Mercator_Mandelbrot_plot.png")</lang>
 
For deep zoom images it is sufficient to calculate a single point with high accuracy. A good approximation can then be found for all other points by means of a perturbation calculation with standard accuracy. See [https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set#Perturbation_theory_and_series_approximation Perturbation Theory] (Wikipedia) and [https://gbillotey.github.io/Fractalshades-doc/math.html Mathematical Background] (Fractalshades) for more details. To reduce glitches, an additional reference sequence for the derivation should be recorded with high precision.
In Julia, unlike Python, it is very easy to extend the zoom range much further than h > 5.5*d by using BigFloats. This is currently not possible with NumPy. However, this is at the expense of computing time, so further optimization of the loop makes sense. Compare also the [https://www.flickr.com/photos/arenamontanus/3380530189/in/album-72157615740829949/ corresponding image] by Anders Sandberg.
<lang julia>using Plots
gr(aspect_ratio=:equal, axis=true, ticks=true, legend=false, dpi=200)
 
setprecision(BigFloat, 128) # set precision to 128 bits (reduced)
setrounding(BigFloat, RoundNearest) # set rounding mode (default)
 
d, h = 200, 2000 # pixel density (= image width) and image height
n, r = 100, 1000 # number of iterations and escape radius (r > 2)
 
a = BigFloat("0.0") # coordinates should be parsed from strings
b = BigFloat("1.0") # compare BigFloat("1.1") and BigFloat(1.1)
 
x = range(zero(a), one(a) * 2, length=d+1)
y = range(zero(b), one(b) * 2 * h / d, length=h+1)
 
A, B = collect(x .* pi), collect(y .* pi)
C = 4.0 .* exp.(.- A .* im)' .* exp.(.- B) .+ a .+ b .* im
 
Z, dZ = zero(C), zero(C)
D = zeros(size(C))
 
abs2_Z, abs2_r = abs2.(Z), abs2(r)
for k in 1:n
M = abs2_Z .< abs2_r
Z[M], dZ[M] = Z[M] .^ 2 .+ C[M], 2 .* Z[M] .* dZ[M] .+ 1
abs2_Z[M] = abs2.(Z[M])
end
 
N = abs.(Z) .> 2 # exterior distance estimation
D[N] = 0.5 .* log.(abs.(Z[N])) .* abs.(Z[N]) ./ abs.(dZ[N])
 
heatmap(D' .^ 0.05, c=:nipy_spectral)
savefig("Mercator_Mandelbrot_map_1.png")</lang>
 
The MultiFloats.jl library can be used to speed up the calculation. To do this, however, the complex exponential function must be broken down into real cosine and sine functions and these functions must be calculated with BigFloats. However, the one-off calculation with a few BigFloats before and after the loop only has a small impact on the calculation speed. Since the number pi is missing from the library, 2*pi is replaced by acos(0)*4. See also the image [https://www.flickr.com/photos/arenamontanus/3430921497/in/album-72157615740829949/ Deeper Mercator Mandelbrot] by Anders Sandberg.
<lang julia>using Plots
gr(aspect_ratio=:equal, axis=true, ticks=true, legend=false, dpi=200)
using MultiFloats
MultiFloats.use_bigfloat_transcendentals()
d, h = 60, 600 # pixel density (= image width) and image height
n, r = 8000, 10000 # number of iterations and escape radius (r > 2)
a = Float64x2("-1.256827152259138864846434197797294538253477389787308085590211144291")
b = Float64x2(".37933802890364143684096784819544060002129071484943239316486643285025")
 
x = range(zero(a), acos(zero(a)) * 4, length=d+1)
y = range(zero(b), acos(zero(b)) * 4 * h / d, length=h+1)
 
A, B = collect(x), collect(y)
C = (.- 4.0) .* (cos.(A) .- sin.(A) .* im)' .* exp.(.- B) .+ a .+ b .* im
 
Z, dZ = zero(C), zero(C)
D = zeros(size(C))
 
abs2_Z, abs2_r = abs2.(Z), abs2(r)
for k in 1:n
M = abs2_Z .< abs2_r
Z[M], dZ[M] = Z[M] .^ 2 .+ C[M], 2 .* Z[M] .* dZ[M] .+ 1
abs2_Z[M] = abs2.(Z[M])
end
 
N = abs.(Z) .> 2 # exterior distance estimation
D[N] = 0.5 .* log.(abs.(Z[N])) .* abs.(Z[N]) ./ abs.(dZ[N])
 
heatmap(D' .^ 0.05, c=:nipy_spectral)
savefig("Mercator_Mandelbrot_map_2.png")</lang>
 
To get even faster, it is enough to calculate a single point with high accuracy. A more or less good approximation can then be found for all other points by means of a perturbation calculation with less accuracy. See [https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set#Perturbation_theory_and_series_approximation Perturbation Theory] (Wikipedia) and [https://gbillotey.github.io/Fractalshades-doc/math.html Mathematical Background] (Fractalshades) for more details. For extreme magnifications, the reference series must also be saved with greater accuracy. All you have to do is change the line ''Zeta = zeros(Complex{Double64}, n)'' and use the DoubleFloats library. Surprisingly, the DoubleFloats are faster than the MultiFloats in this context. In order to reduce the image errors, an additional reference sequence for the derivations should be recorded with high accuracy.
<lang julia>using Plots
gr(aspect_ratio=:equal, axis=true, ticks=true, legend=false, dpi=200)
Line 6,582 ⟶ 6,512:
setrounding(BigFloat, RoundNearest) # set rounding mode (default)
 
d, h = 50200, 1000150 # pixel density (= image width) and image height
n, r = 50000, 100000 # number of iterations and escape radius (r > 2)
 
Line 6,603 ⟶ 6,533:
y = range(0, 2 * h / d, length=h+1)
 
A, B = collect(x .*- pi1), collect(y .*- pih / d)
C = (5.0e- 4.0)55 .* exp.((A' .+ B .* im) .* im) # displacement (C = Delta)
 
Z, dZ, E, dE = zero(C), zero(C), zero(C), zero(C) # differences (E = Epsilon, dE = dEpsilon)
Line 6,618 ⟶ 6,548:
D[N] = 0.5 .* log.(abs.(Z[N])) .* abs.(Z[N]) ./ abs.(dZ[N])
 
heatmap(D' .^ 0.02525, c=:nipy_spectral)
savefig("Mercator_Mandelbrot_deep_mapMandelbrot_deep_zoom.png")</lang>
 
Of course, deep Mercator maps can also be created. See also the image [https://www.flickr.com/photos/arenamontanus/3430921497/in/album-72157615740829949/ Deeper Mercator Mandelbrot] by Anders Sandberg.
Of course, individual deep zoom images can also be created.
<lang julia>using Plots
gr(aspect_ratio=:equal, axis=true, ticks=true, legend=false, dpi=200)
Line 6,628 ⟶ 6,558:
setrounding(BigFloat, RoundNearest) # set rounding mode (default)
 
d, h = 16060, 1201200 # pixel density (= image width) and image height
n, r = 5000060000, 100000 # number of iterations and escape radius (r > 2)
 
a = BigFloat("-1.256827152259138864846434197797294538253477389787308085590211144291")
Line 6,650 ⟶ 6,580:
 
A, B = collect(x .* pi), collect(y .* pi)
C = 2(.0e-55 4.0) .* exp.((A' .+ B .* im) .* im) # displacement (C = Delta)
 
Z, dZ, E, dE = zero(C), zero(C), zero(C), zero(C) # differences (E = Epsilon, dE = dEpsilon)
Line 6,664 ⟶ 6,594:
D[N] = 0.5 .* log.(abs.(Z[N])) .* abs.(Z[N]) ./ abs.(dZ[N])
 
heatmap(D' .^ 0.25025, c=:nipy_spectral)
savefig("Mandelbrot_deep_zoomMercator_Mandelbrot_deep_map.png")</lang>
 
The MultiFloats.jl library can be used to verify the results. To do this, however, the complex exponential function must be broken down into real cosine and sine functions and these functions must be calculated with BigFloats. However, the one-off calculation with a few BigFloats before and after the loop only has a small impact on the calculation speed. Since the number pi is missing from the library, 2*pi is replaced by acos(0)*4. Although the calculation with MultiFloats is much faster than with BigFloats, it is much slower compared to the perturbation calculation.
<lang julia>using Plots
gr(aspect_ratio=:equal, axis=true, ticks=true, legend=false, dpi=200)
using MultiFloats
MultiFloats.use_bigfloat_transcendentals()
d, h = 10, 200 # pixel density (= image width) and image height
n, r = 60000, 100000 # number of iterations and escape radius (r > 2)
a = Float64x3("-1.256827152259138864846434197797294538253477389787308085590211144291")
b = Float64x3(".37933802890364143684096784819544060002129071484943239316486643285025")
 
x = range(zero(a), acos(zero(a)) * 4, length=d+1)
y = range(zero(b), acos(zero(b)) * 4 * h / d, length=h+1)
 
A, B = collect(x), collect(y)
C = (.- 4.0) .* (cos.(A) .- sin.(A) .* im)' .* exp.(.- B) .+ a .+ b .* im
 
Z, dZ = zero(C), zero(C)
D = zeros(size(C))
 
abs2_Z, abs2_r = abs2.(Z), abs2(r)
for k in 1:n
M = abs2_Z .< abs2_r
Z[M], dZ[M] = Z[M] .^ 2 .+ C[M], 2 .* Z[M] .* dZ[M] .+ 1
abs2_Z[M] = abs2.(Z[M])
end
 
N = abs.(Z) .> 2 # exterior distance estimation
D[N] = 0.5 .* log.(abs.(Z[N])) .* abs.(Z[N]) ./ abs.(dZ[N])
 
heatmap(D' .^ 0.025, c=:nipy_spectral)
savefig("Mercator_Mandelbrot_test_map.png")</lang>
 
=={{header|Kotlin}}==
305

edits