Saturday, July 21, 2012

mandelbrot 1

A few weeks ago I found a book called "Computer Kurzweil" ("fun with computers") in my brother's room back at our parents' place. It looks like from the late 80s, but just for the fun of it I took it with me. The first chapter is on the mandelbrot set and visualising it. The Mandelbrot set is given by:
\[M = \{c \in \mathbb{C} | lim_{n \to \infty}z_{n+1} = z_n + c \ne \infty\}\]
with \(z_0=0\). Above definition is a sloppy way of saying that any complex number \(c\) for which \(z_n\) is bound is part of the Mandelbrot set. Moreover the absoulte value \(|z_n>|\) is bound, which will be neccessary for our computation.
For numerical calculation we need to think of a criterion for deciding whether or not a complex number is part of the Mandelbrot set. The following condition is to be fulfilled:
\[|z_{1000}| < 2\]
In general instead of looking at \(|z_{1000}|\) a parameter maxIter should be used: \(|z_{\textrm{maxIter}}|\).
The "score" a complex number gets is the number n of iterations, until \(|z_n| \ge 2\). If it never gets as big as 2, maxIter will be the score.
That is all we need to know before getting started with the code. We will need to make use of complex numbers. You can use the complex numers library included in c++, however, I decided to create my own class Complex, primarily to learn operator overload. The class is not finished yet, but it works well as far as the Mandelbrot set is concerned (a blog post will follow as soon as it's done).

The class Mandelbrot contains 8 private member variables:

class Mandelbrot {
private:
  double xmin_;
  double xmax_;
  double ymin_;
  double ymax_;
  int stepsX_;
  int stepsY_;
  int maxIter_;
  std::vector<std::vector< double > > *grid_;

xmin_, xmax_, ymin_, ymax_ determine the rectangle in \(\mathbb{C}\) where the Mandelbrot set is to be calculated, stepsX_ and stepsY_ give the number of intercepts on the grid, maxIter_ gives the maximum \(n\) for \(z_n\). The scores are stored in grid_.

The member functions contain constructors, a destructor, various functions to set the parameters and a function to fill the grid as well as another one to write the grid to a file. I only will present the latter here as the others are not interesting and their implementation is obvious.

public:
  ...
  void fillGrid();
  bool writeToFile(const char *filename) const;
};

The basic concept of fillGrid() is to iterate over all points in the grid defined by the parameters and then get the score for each point. This is quite simple. I used \(|z_n|^2\) instead of \(|z_n|\) to save computation time. The score is divided by maxIter_ to obtain scores in the interval \([0,1]\).
void Mandelbrot::fillGrid() {
  if (!grid_) {
    delete grid_;
    grid_ = 0;
  }
  grid_ = new std::vector<std::vector<double > >(stepsY_, std::vector<double>(stepsX_));
  double stepSizeX = (xmax_ - xmin_)/stepsX_;
  double stepSizeY = (ymax_ - ymin_)/stepsY_;
  for (int y = 0; y < stepsY_; y++) {
    double im = ymin_ + y*stepSizeY;
    for (int x = 0; x < stepsX_; x++) {
      double re = xmin_ + x*stepSizeX;
      Complex c(re, im);
      Complex z(0, 0);
      int iterations = maxIter_;
      while (iterations > 0) {
 if (z.sqAbs() >= 4) {
   break;
 }
 z = z*z;
 z = z + c;
 iterations--;
      }
      double gridValue = 1.0*iterations/maxIter_;
      grid_->at(y).at(x) = gridValue;
    }
  }
}

Instead of writing the score grid to an image file, I have only implemented a function to write it to a text file (CSV Style with spaces as delimiters). This file can then be read using matlab, Python, ... to easily show the image. An implementation of an export function for images will follow. The function writeToFile(const char*) simply uses std::ofstream to write to the file.

bool Mandelbrot::writeToFile(const char *filename) const {
  if (!grid_) {
    return 0;
  }
  std::ofstream f(filename, std::ios::trunc);
  if (f.is_open()) {
    for (int y = 0; y < stepsY_; y++) {
      for (int x = 0; x < stepsX_; x++) {
 f << grid_->at(y).at(x) << " ";
      }
      f << "\n";
    }
    f.close();
  }
  else {
    return 0;
  }
  return 1;
}

In order to prevent overwriting of existing files, a test to check if a file at the specified filename already exists needs to be implemented as well.

Overall the implementation is easy and one can get cool images when looking at the right regions of \(\mathbb{C}\). In order to enhance results raising the scores \(\in [0,1]\) to a higher power is a good idea.
The source code can be found on github.


The two images on the left show the same region (\(-1.26 \le \Re \le-1.245\), \(0 \le \Im \le 0.03\)) using just the scores (grayscale) or mapping them to a colormap. In both images the scores are raised to the power of 4. The github repository contains a Makefile. You can find interesting regions on your own with that.
Future work on the Mandelbrot Class will include the option to write an image as ouput in addition to the CSV file, the file check mentioned above as well as an argument parser so the parameters can be set without compiling each time.

I found another interesting region: \(-0.72 \le \Re \le -0.7\), \(0.24 \le \Im \le 0.26 \)






Mathjax

I found a way to implement tex/latex in my blog posts: http://www.mathjax.org/ Details are given on the website.

Friday, July 20, 2012

github repository

Anything that I write to practice c++ goes here:
https://github.com/hanslovsky/exercise

First Post

This blog is mainly for keeping track of my progress in learning C++. I will upload code that I use for practice and write on the difficulties I had getting there. Comments are more than welcome and I'll be happy to receive hints or if you point out where I got something wrong. I will also link to my github repository where I put my practice code.

Occasionally I will also comment on things I consider interesting, that are not neccessarily related to programming.