Skip to main content

graphics - Speeding up this fractal-generating code


I used the code below (which is a sample from this gist containing more similar code) in my answer to my own question about Mandelbrot-like sets for functions other than the simple quadratic on Math.SE to generate this image:


graphic


cosineEscapeTime = 
Compile[{{c, _Complex}},
Block[{z = c, n = 2, escapeRadius = 10 \[Pi], maxIterations = 100},
While[And[Abs[z] <= escapeRadius, n < maxIterations],
z = Cos[z] + c; n++]; n]]


Block[{center = {0.5527, 0.9435}, radius = 0.1},
DensityPlot[
cosineEscapeTime[x + I y], {x, center[[1]] - radius,
center[[1]] + radius}, {y, center[[2]] - radius,
center[[2]] + radius}, PlotPoints -> 250, AspectRatio -> 1,
ColorFunction -> "TemperatureMap"]]

What could I do to improve the speed/time-efficiency of this code? Is there any reasonable way to parallelize it? (I'm running Mathematica 8 on an 8-core machine.)




edit Thanks all for the help so far. I wanted to post an update with what I'm seeing based on the answers so far and see if I get any further refinements before I accept an answer. Without going to hand-written C code and/or OpenCL/CUDA stuff, the best so far seems to be to use cosineEscapeTime as defined above, but replace the Block[...DensityPlot[]] with:



Block[{center = {0.5527, 0.9435}, radius = 0.1, n = 500},
Graphics[
Raster[Rescale@
ParallelTable[
cosineEscapeTime[x + I y],
{y, center[[2]] - radius, center[[2]] + radius, 2 radius/(n - 1)},
{x, center[[1]] - radius, center[[1]] + radius, 2 radius/(n - 1)}],
ColorFunction -> "TemperatureMap"], ImageSize -> n]
]


Probably in large part because it parallelizes over my 8 cores, this runs in a little under 1 second versus about 27 seconds for my original code (based on AbsoluteTiming[]).



Answer



Use these 3 components: compile, C, parallel computing.


Also to speed up coloring instead of ArrayPlot use


Graphics[Raster[Rescale[...], ColorFunction -> "TemperatureMap"]]

In such cases Compile is essential. Compile to C with parallelization will speed it up even more, but you need to have a C compiler installed. Note difference for usage of C and parallelization may show for rather greater image resolution and more cores.


mandelComp = 
Compile[{{c, _Complex}},
Module[{num = 1},

FixedPoint[(num++; #^2 + c) &, 0, 99,
SameTest -> (Re[#]^2 + Im[#]^2 >= 4 &)]; num],
CompilationTarget -> "C", RuntimeAttributes -> {Listable},
Parallelization -> True];

data = ParallelTable[
a + I b, {a, -.715, -.61, .0001}, {b, -.5, -.4, .0001}];

Graphics[Raster[Rescale[mandelComp[data]],
ColorFunction -> "TemperatureMap"], ImageSize -> 800, PlotRangePadding -> 0]


enter image description here


This is just a prototype - you can figure out a better coloring. Another way is to use LibraryFunction - we have Mandelbrot built in:


mlf = LibraryFunctionLoad["demo_numerical", "mandelbrot", {Complex}, 
Integer];
n = 501; samples =
Table[mlf[x + I y], {y, -1.25, 1.25, 2.5/(n - 1)}, {x, -2., .5,
2.5/(n - 1)}];
colormap =
Function[If[# == 0, {0., 0., 0.}, Part[r, #]]] /.

r -> RandomReal[1, {1000, 3}];
Graphics[Raster[Map[colormap, samples, {2}]], ImageSize -> 512]

enter image description here


Now, if you have a proper NVIDIA graphics card you can do some GPU computing with CUDA or OpenCL. I use OpenCL here because I got the source (from documentation btw):


Needs["OpenCLLink`"]

src = "
__kernel void mandelbrot_kernel(__global mint * set, float zoom, \
float bailout, mint width, mint height) {

int xIndex = get_global_id(0);
int yIndex = get_global_id(1);
int ii;

float x0 = zoom*(width/3 - xIndex);
float y0 = zoom*(height/2 - yIndex);
float tmp, x = 0, y = 0;
float c;

if (xIndex < width && yIndex < height) {

for (ii = 0; (x*x+y*y <= bailout) && (ii < MAX_ITERATIONS); \
ii++) {
tmp = x*x - y*y +x0;
y = 2*x*y + y0;
x = tmp;
}
c = ii - log(log(sqrt(x*x + y*y)))/log(2.0);
if (ii == MAX_ITERATIONS) {
set[3*(xIndex + yIndex*width)] = 0;
set[3*(xIndex + yIndex*width) + 1] = 0;

set[3*(xIndex + yIndex*width) + 2] = 0;
} else {
set[3*(xIndex + yIndex*width)] = ii*c/4 + 20;
set[3*(xIndex + yIndex*width) + 1] = ii*c/4;
set[3*(xIndex + yIndex*width) + 2] = ii*c/4 + 5;
}
}
}
";


MandelbrotSet =
OpenCLFunctionLoad[src,
"mandelbrot_kernel", {{_Integer, _, "Output"}, "Float",
"Float", _Integer, _Integer}, {16, 16},
"Defines" -> {"MAX_ITERATIONS" -> 100}];

width = 2048;
height = 1024;
mem = OpenCLMemoryAllocate[Integer, {height, width, 3}];


res = MandelbrotSet[mem, 0.0017, 8.0, width, height, {width, height}];

Image[OpenCLMemoryGet[First[res]], "Byte"]

enter image description here


References:


Fractals CDF paper


Compile to C


LibraryFunction


OpenCL



Demonstrations


Comments

Popular posts from this blog

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Adding a thick curve to a regionplot

Suppose we have the following simple RegionPlot: f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}] Now I'm trying to change the curve defined by $y=g[x]$ into a thick black curve, while leaving all other boundaries in the plot unchanged. I've tried adding the region $y=g[x]$ and playing with the plotstyle, which didn't work, and I've tried BoundaryStyle, which changed all the boundaries in the plot. Now I'm kinda out of ideas... Any help would be appreciated! Answer With f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 You can use Epilog to add the thick line: RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}, PlotPoints -> 50, Epilog -> (Plot[g[x], {x, 0, 2}, PlotStyle -> {Black, Thick}][[1]]), PlotStyle -> {Directive[Yellow, Opacity[0.4]], Directive[Pink, Opacity[0.4]],