Generating cool fractals

A benchmark comparison of PDL, IDL, MATLAB, Octave, C and FORTRAN77 generating fractals

Download the whole article as PDF

Short URL: http://fsmsh.com/2108

Write a full post in response to this!


Whether you are a professional or amateur scientist, engineer or mathematician, if you need to make numerical calculations and plots quickly and easily, then PDL (Perl Data Language) is certainly one of the best free software tools to use. PDL has everything that similar high-level, proprietary, numerical calculation languages (like IDL or MATLAB) have. And it certainly comes with all the features you would expect to have in a numerical calculation package.

Introduction

In this article, I will explore a fun application of the numerical calculation field: the Mandelbrot fractal. To generate fractals you need to perform several computations and then plot the results. The examples that follow are also simple enough for anyone to follow, but have enough features to get a feel for the language used. For this particular task, high-level, numerical calculation languages are the ideal tool. They provide a rapid development cycle due to its high-level nature. It is also easy to generate quality plots with these tools. And, as you shall see later, the speed performance penalty you pay by using these languages is relatively minor.

The Mandelbrot fractal example reviewed in this article will be developed in both proprietary and free software languages: PDL, IDL, MATLAB and Octave. This will allow us to compare them qualitatively in a table highlighting the pros and cons of each language. Also, a quantitative benchmark comparison will be done to illustrate the speed of each one of the languages. For comparison purposes, C and FORTRAN code for these examples will also be shown.

The Mandelbrot fractal

The Mandelbrot set is one of the most popular fractals

According to Wikipedia, a fractal is a rough or fragmented geometric shape that can be subdivided into parts, each of which is (at least approximately) a reduced size copy of the whole. The Mandelbrot set is one of the most popular fractals. In this article I will color the fractals in a slightly different and artistic way (as seen in figure 1). The Mandelbrot fractal is generated by iterating a mathematical relationship between complex numbers (see Complex Number Arithmetic for a quick introduction to complex numbers). Starting with a zero value of a complex variable (z), the fractal is materialised by iterating the following assigment:

z = z * z + k,

…where k is a complex number which is kept constant throughout the iterations. The final value of z depends very much on the value of the complex number k. For some values of k, the final z converges to a certain finite value and for some others, z diverges to infinity. To avoid numerical problems with the divergences in the calculations we will introduce a programming trick that consists of constraining the z values between -5 and 5 after each iteration. In the final fractal image, the x axis will represent the real values of k and the y axis its imaginary values. In the Mandelbrot fractal, normally the divergent z values are given one color and the convergent ones another. The frontier between one color and the other is a fractal. Here I will color the images using a rainbow palette to plot the values of the formula:

image = log (sqrt(Re(z) * Re(z) + Im(z) * Im(z)) + 1)

…where Re and Im denote the real and imaginary part of z respectively. The final Madelbrot fractal image can be seen in figure 1.

Figure 1: the Mandelbrot fractal
Figure 1: the Mandelbrot fractal

PDL

PDL is an extension of the Perl language for number crunching and scientific plotting

PDL is an extension of the Perl language for number crunching and scientific plotting. Because of its Perl heritage it has the clean syntax of a well settled programming language. You can also use one of the wealth of available module-packages in the Perl archive (CPAN), to use the language for other purposes like database access, date handling, inter-process communication, GUI toolkit, just to name a few.

Installing PDL

Unfortunately, PDL is not installed by default on many of the popular GNU/Linux distributions. Even worse, some distributions have the PDL related packages missing or partly broken. If you are running a GNU/Linux distribution based on Debian (Ubuntu, Knoppix, etc.), and have access to the regular Debian repositories, it is fairly easy to install PDL. You should install the pdl, pgplot5 and pgperl packages in order to follow the examples in this article. This can be done (as root) via the usual:

 
apt-get install pdl pgplot5 pgperl 

If this is not your case, follow the instructions in [Installing PDL the quick and easy way](http://wiki.jach.hawaii.edu/pdl_wiki-bin/wiki/Getting_Started_with_PDL/Installing_PDL_the quick and easy way).

The PDL command line

Once you have PDL installed you should be able to run it in the command line. For this just type perldl on a terminal.

It should greet you with a message like:

xcalbet@debian:~$ perldl
perlDL shell v1.33
PDL comes with ABSOLUTELY NO WARRANTY. For details, see the file 'COPYING' in the PDL distribution. This
is free software and you are welcome to redistribute
it under certain conditions, see the same file for
details.
ReadLines, NiceSlice, MultiLines  enabled
Reading PDL/default.perldlrc...
Found docs database /usr/local/lib/perl/5.8.4/PDL/pdldoc.db
Type 'help' for online help
Type 'demo' for online demos
Loaded PDL v2.4.2
perldl>

You can try out now some of PDL features by typing demo after the PDL prompt:

 
perldl> demo 
Don't miss out on the other pages!
1234567next ›last »

Write a full post in response to this!

0

Do you like this post?
Vote for it!

Copyright information

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 2 or any later version published by the Free Software Foundation. A copy of the license is available at http://www.gnu.org/copyleft/gpl.html.

Biography

Xavier Calbet: Xavier Calbet (xcalbet AT googlemail DOT com) is a long time free software user who started using Linux distributions in the ancient times when Slackware had to be installed using tens of floppy disks. He is currently working on his two pet projects: a meteorological field and satellite image display system, SAPO, and the best available free numerical computer language to date, PDL (Perl Data Language).

Anonymous visitor's picture

AWESOME!!!

Submitted by Anonymous visitor (not verified) on Tue, 2007-06-05 07:30.

Vote!
0

Very nice article! I've never heard of PDL, though I do computational math (not numerical) everyday. I'm definitely going to find a use for it now.

mWo's picture

Matalb and Octave code

Submitted by mWo on Tue, 2007-06-05 10:02.

Vote!
0

Last line in MATLAB and Octave code, i.e., 'exit' causes that Matlab exits; hence, it is not possible to look at this fractal image.
mWo

Dan S.'s picture

It helps to know the problem you're solving a bit better.

Submitted by Dan S. (not verified) on Tue, 2007-06-05 15:53.

Vote!
0

Let me preface this by saying that PDL looks neat. But,

You're iterating over the whole image with every iteration, doing gobs of work for pixels/locations that have diverged. I don't believe that there is any useful information retained by your clamping-to-5 step. (It is pretty, though!) With Mandelbrot, if the distance from the origin is > 2 then divergence to infinity is inevitable. The Mandelbrot set itself is defined as those starting values which never diverge to infinity; I think but am not sure that your clamping makes it impossible to tell even what is in/out of the set.

(( I believe the canonical/typical coloring is based on #-of-iterations-before-divergence for those pixels that diverge and black for those that don't. ))

Here is a patch (sorry about lost whitespace/indenting):

37,39c37,39
< for(k=0;k < niter;k++){
< for (i=0;i < NPTS;i++) {
< for (j=0;j < NPTS;j++) {
---
> for (i=0;i < NPTS;i++) {
> for (j=0;j < NPTS;j++) {
> for(k=0;k < niter;k++){

48,52c48,51
< if (zRe[i][j] < -5.) zRe[i][j]=-5.;
< if (zRe[i][j] > 5.) zRe[i][j]=5.;
< if (zIm[i][j] < -5.) zIm[i][j]=-5.;
< if (zIm[i][j] > 5.) zIm[i][j]=5.;
---
> if (zRe[i][j]* zRe[i][j] + zIm[i][j]*zIm[i][j] > 4.) // Divergence criteria
> break;

How much improvement? 2000x2000 array of points, 1000 iterations, my laptop/gcc 4.1.2 (Note: I had to add 'static' to the local arrays to make it work):
- Old: 4m46.654s
- New: 0m16.373s

C wins again! ;)

I don't know how to get this speedup effect in any of the "whole-array" approaches/languages. (Probably because I don't know them well at all.) It seems hard to get any effect other than foreach(iteration) { foreach(location in image) { /*..do something.. */ } } ; with this order of operations there's no way to bail out of iterations early at diverging locations.

I would speculate that, for this problem, plain old perl would be faster than your PDL approach... ;)

David Bateman's picture

Poor matlab/octave implementation

Submitted by David Bateman (not verified) on Tue, 2007-06-05 20:35.

Vote!
0

If you replace the lines like

qgtfive= find(qRe > 5.);
zRe(qgtfive)=5.;

with

zRe(qRe>5)=5

at least in Octave the code is about twice as fast. I believe the same indexing trick can be used in recent versions of matlab as well. You are basically getting your speed improvement from the PDL clip function.

D.

David Bateman's picture

Dan, Something like

Submitted by David Bateman (not verified) on Tue, 2007-06-05 20:51.

Vote!
0

Dan,

Something like

function z = mandel
npts=1000;
niter=51;
z=zeros(npts,npts);
[kRe,kIm] = meshgrid(linspace(-1.5,.5,npts),linspace(-1,1,npts));
k = kRe + 1i*kIm;
idx = [1:npts*npts];
while (niter)
z(idx) = z(idx).^ 2 + k(idx);
idx(abs(z(idx))>2) = [];
niter = niter - 1;
end
end

will do it for Octave/Matlab. However, at least in Octave the above indexed assignment can be quite expensive, and so the above is actually slower for this example. It might be faster with matlab...

D.

Alexander Barth's picture

indexed assignment

Submitted by Alexander Barth (not verified) on Tue, 2007-06-05 22:01.

Vote!
0

Hi David and Dan,

Here too, you can avoid indexed assignment by using a logical mask which is much faster in octave.

Cheers,
Alex

octave:7> tic; z=mandel(); toc;
Elapsed time is 43.521179 seconds.
octave:8> tic; z2=mandel2(); toc;
Elapsed time is 24.081490 seconds.
octave:9> max(abs(z(:)-z2(:)))
ans = 0

function z = mandel2
npts=1000;
niter=51;
z=zeros(npts,npts);
[kRe,kIm] = meshgrid(linspace(-1.5,.5,npts),linspace(-1,1,npts));
k = kRe + 1i*kIm;

while (niter)
mask = abs(z) < 2;
z(mask) = z(mask).^ 2 + k(mask);

niter = niter - 1;
end

Dan S.'s picture

David,

Submitted by Dan S. (not verified) on Tue, 2007-06-05 22:39.

Vote!
0

That looks like it might work. Do octave/matlab have a way to apply a function to each element of a 2d array? Then the solution becomes very nice and clear, just creating a per-pixel 'mandelbrot' function and then applying it to each element of the (complex) image matrix.

Anonymous visitor's picture

Fortran 95 is a huge improvement over Fortran 77

Submitted by Anonymous visitor (not verified) on Tue, 2007-06-05 23:53.

Vote!
0

gcc 4.x now provides for Fortran 90/95, which has a syntax similar to Matlab/Octave. A good Fortran 95 compiler should provide the ease of development of Matlab, with the speed quoted for Fortran 77. While you have the claim that Fortran is not rich in IO & maths functions you omit that there is more maths code in Fortran than in any other language - Netlib and Statlib being two of the best examples. I agree that Fortran is not terribly good for graphical IO.

Rafael Verduzco's picture

I think C wins

Submitted by Rafael Verduzco on Wed, 2007-06-06 02:20.

Vote!
0

Personally, i think C wins. I have to be honest and say that as a first approach to explore the problem I use the Matlab that my university provides me, but when it comes to flexibility, control, speed of execution and the ability to share code with my peers at the university, then C is the choice. Needless to say that there are available free paralelization tools to Fortran and C, and even though they need high computer skills, let's face it: if you are coping with mathematical, numerical computation software, high chances are that you are computer skilled.
I know, there's a lot of people (biologists, finance people, etc.) But you should look at the present in science, sneak into the current research journals and note that there's a tendency to people get more and more skilled in performing in silico simulations, coming from very different fields, not only math or computer science.
Nah!, IMHO, C (Fortran, if you like it better) win.

Brandon Andrews's picture

A different c implementation.

Submitted by Brandon Andrews (not verified) on Thu, 2007-06-07 03:52.

Vote!
0

Compile with: gcc -lm -lpng mandlebrot.c .I used gcc 4.2.0.
#include <stdio.h>
#include <math.h>
#include <png.h>
#define NPTS 1000 //=width=height
png_uint_16 img[NPTS][NPTS][3];
void genImg();
void addPixel();
void writePng();
int main(void){ genImg(); writePng(); return 0; }
void genImg() {
int i,j,k,niter;
double zRe, zIm, kRe, kIm, qRe, qIm;
niter=51; // Number of iterations
for (i=0;i<NPTS;i++) {
for (j=0;j<NPTS;j++) {
// Generating z = 0 (real and imaginary part)
zRe=0.;
zIm=0.;
// Generating the constant k (real and imaginary part)
kRe=(double)i*2.0/((double)NPTS-1.)-1.5;
kIm=(double)j*2.0/((double)NPTS-1.)-1.;
//Iterating
for (k=0;k<niter;k++){
// Calculating q = z*z + k in complex space
// q is a temporary variable to store the result
qRe=zRe*zRe-zIm*zIm+kRe;
qIm=2.*zRe*zIm+kIm;
// Assigning the q values to z constraining between
// -10 and 10 to avoid numerical divergences
zRe=qRe;
zIm=qIm;
if (zRe < -10.) zRe=-10.; if (zRe > 10.) zRe=10.; if (zIm < -10.) zIm=-10.; if (zIm > 10.) zIm=10.;
}
addPixel(i,j,zRe,zIm);
}
}
}
void addPixel(int i, int j, double zRe, double zIm){
img[i][j][0] = (png_uint_16)(sin(log( sqrt(zRe*zRe+zIm*zIm)))*65536);
img[i][j][1] = (png_uint_16)(cos(log( sqrt(zRe*zRe+zIm*zIm)))*65536);
img[i][j][2] = (png_uint_16)(log( sqrt(zRe*zRe+zIm*zIm))*65536);
}
void writePng(){
FILE *fp = fopen("mandle.png", "wb");
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
png_infop info_ptr = png_create_info_struct(png_ptr);
png_set_IHDR(png_ptr, info_ptr, NPTS, NPTS, 16, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_init_io(png_ptr, fp);
png_write_info(png_ptr, info_ptr);
png_set_swap(png_ptr); //big -> little endian
int i; for (i=0;i<NPTS;i++){ png_write_row(png_ptr, (png_bytep)img[i]); }
png_write_end(png_ptr,info_ptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
}

Fzzy's picture

Other useful linux fractal resources

Submitted by Fzzy on Tue, 2007-07-31 12:02.

Vote!
0

Well, i know nothing of all this math. How do i get up my brownie points? Here are some interesting Linux fractal resources:

1. A cool open source screensaver which also uses fractal math to do it's thing. (http://electricsheep.org/)

2. Gnofract. The author says he think's it's the best fractal package currently available for Linux. Test it and decide for yourself (http://gnofract4d.sourceforge.net/)

3. For the purists, fractals are about math. For the arty, here's some fun fractal stuff you can do in GIMP (http://gentoo-wiki.com/TIP_GIMP_Fractal_Backgrounds)

4. Fyre is a tool for producing computational artwork based on histograms of iterated chaotic functions. Source is included on the site. (http://fyre.navi.cx/)

Anonymous visitor's picture

Some Remarks

Submitted by Anonymous visitor (not verified) on Tue, 2007-07-31 13:41.

Vote!
0

Fortran has a complex data type which would be appropriate here.
Also there is a FOSS implementation of IDL called GNUDL.
An finally R is a very interesting language that has not been looked at in this
comparison.



CariNet: Cloud computing is a reality.

Other sites

Odiogo

Free Software Magazine uses Apollo, project management and CRM for its everyday activities!