Mandelbrot Set - notes on the program

The option provided by the JLayer Framework to have the method of a unit class executed in parallel on the available processor cores only offers an advantage if the execution of this method firstly has no side effects and secondly takes a lot of time.

These requirements are basically met if one wants to visualize the Mandelbrot set, and with the help of the present Mandelbrot program it is possible to compare the loop implementation with the parallel implementation. In this sample program, the linking of objects is irrelevant.


Unit Class MandelbrotUnit - Using JLayer Annotations

In order to calculate the color of a pixel in the image (locally), the overall size of the image and the position of the pixel must be known. In addition, a maximum limit for an iteration to be performed must be specified. All these things are reflected in the MandelbrotUnit class code as follows.

In addition to the static fields width, height and maxIterations, you find the static field palette, which is used to store RGB values, and all these fields are set using the static setGlobalParams() method.

Layer field index has the annotation @LayerField(isIndex = true) and this ensures that the correct index is set automatically when wrapping object arrays of type MandelbrotUnit, and layer field rgbValue is used to store the computed color.


  @LayerUnit
  public class MandelbrotUnit {
	
    private static int width, height;
    private static int maxIterations;
    private static int[] palette;
	
    @LayerField(isIndex = true)
    int[] index;
	
    @LayerField
    int rgbValue;
	
    static void setGlobalParams(int width, int height, int maxIterations) {
      MandelbrotUnit.width = width;
      MandelbrotUnit.height = height;
      MandelbrotUnit.maxIterations = maxIterations;

      palette = new int[256];
      for (int i = 0; i < 256; i++)
        palette[i] = Color.getHSBColor(i/255F, 1, 1).getRGB();
    }

    private int countIterations(int row, int col) {
      double xmin, xmax, ymin, ymax;
      xmin = -1.6744096740931858;
      xmax = -1.674409674093473;
      ymin = 4.716540768697223E-5;
      ymax = 4.716540790246652E-5;

      double x, y;
      double dx, dy;
      dx = (xmax-xmin)/(width-1);
      dy = (ymax-ymin)/(height-1);
      y = ymax - dy*row;
      x = xmin + dx*col;

      int count = 0;
      double xx = x;
      double yy = y;
      while (count < maxIterations && (xx*xx + yy*yy) < 4) {
        count++;
        double newxx = xx*xx - yy*yy + x;
        yy = 2*xx*yy + y;
        xx = newxx; 
      }
      return count;
    }
	
    private int getRGB(int count) {
      if (count == maxIterations)
        return 0;
      else
        return palette[count%palette.length];
    }

    private void setRgbValue() {
      int row = index[0];
      int col = index[1];
      int count = countIterations(row, col);
      rgbValue = getRGB(count);
    }
	  
    @LayerMethod
    void setRgbValue_LOOP(){
      setRgbValue();
    }
	
    @LayerMethod(runtimeMode=RuntimeMode.FORKJOIN)
      void setRgbValue_PARALLEL(){
      setRgbValue();
    }

  }
  

At the bottom you find two annotated methods setRgbValue_LOOP() and setRgbValue_PARALLEL() that actually cause the setRgbValue() method to loop or run in parallel. Note that setRgbValue() needs to know its position - row and col - which is passed as parameter to method countIterations(), the execution of which is the brunt of the calculation.


Establishing Global Structure and Dynamics - Using the Generated Wrapper Class

The MandelbrotNet class executes the color calculations in a loop or in parallel and also establishes a connection to the graphic display. The parts of the code that depend on the JLayer framework are color coded.

At the top are the definitions of the global parameters mentioned above, the declaration of an array of MandelbrotUnits and its associated wrapper object mandelbrotLayer, and the declaration of the mandelbrotImage field that serves as the interface to the graphical representation. Next, in method createNetwork(), everything is initialized.


  public class MandelbrotNet {
	
    int width = 1600;
    int height = 1200;
    int maxIterations = 10000;

    MandelbrotUnit[][] mandelbrotArray = null;
    Layer_MandelbrotUnit_ mandelbrotLayer = null; 

    BufferedImage mandelbrotImage = null;

    void createNetwork() {

      MandelbrotUnit.setGlobalParams(width, height, maxIterations);

      mandelbrotArray = new MandelbrotUnit[height][width];
      for(int row = 0; row < height; row++) {
        for(int col = 0; col < width; col++) {
          mandelbrotArray[row][col] = new MandelbrotUnit();
        }
      }   
      mandelbrotLayer = new Layer_MandelbrotUnit_(mandelbrotArray); 

      mandelbrotImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
      Graphics g = mandelbrotImage.createGraphics(); // fill image with gray
      g.setColor(Color.LIGHT_GRAY);
      g.fillRect(0,0,width,height);
      g.dispose();

    }
	
    private void updateImage() {
      for(int row = 0; row < height; row++) {
        for(int col = 0; col < width; col++) {
          mandelbrotImage.setRGB(col, row, mandelbrotLayer.rgbValue.get(row, col)); 
        }
      }   
    }

    void setRGB_LOOP() {
      mandelbrotLayer.setRgbValue_LOOP(); 
      updateImage();
    }

    void setRGB_PARALLEL() {
      mandelbrotLayer.setRgbValue_PARALLEL(); 
      updateImage();
    }
	
  }
  

The two methods setRGB_LOOP() and setRGB_PARALLEL() at the end trigger the sequential or parallel calculation of the colors and afterwards update the graphical interface.


More Information