GoMandlebrodt

This project contains a go and a python file used to both generate and display various gifs of the mandlebrodt set respectively. It was written as a final project for distributed computing (CS 315) at Whitworth University in 2018. This readme was made by using this helpful webiste! All of the code was written by Jude Battista and Cameron Rutherford respectively.

Note: We are by no means mathematicians, let alone experts on the mandlebrot set. For more information about calculating if a point is in the set, or information about the set please see other sources such as this one or this one.

Attatched to this repo is also a folder called progress gifs. They are not particularly well named, but they are some interesting outputs that we had gotten by running our code. For example of interesting coordinates to zoom in on, please again see other websites (such as this).

Installation

Use the package manager pip to install both numpy and array2gif.

pip install numpy
pip install array2gif

Also ensure that you are able to run powershell scripts on your machine. To do this you may have to run "set-executionpolicy unrestricted".

Usage

Note: If the name of the python file is changes from test.py, you may need to modify the shell script, and keep that in mind reading the rest of this documentation.

Running the following command in powershell will create a gif called test.gif in your current working directory. The script can also be modified to work with bash as well, but right now it only works in powershell.

.\BuildGif.ps1

Also note that several text files with information about each frame will be created in the current working directory to pass information between go and python. These files can be quite large, and are generated by the go program, and read by the python script. This means that if you have already generated the data for the frames, and would like to just re-color the image/modify something in the python script and run it again on the same data you can just run the following and avoid re-generating the data. Do note that a gif can only have 256 different colors in it, and so be careful about modifying iterations_to_RGB, and read the commments when doing so:

py ./test.py

When modifying various parameters regarding what will eventually be in the final gif, make sure that when you modify the following ones, you do so carefully. If you modify them in one file that you must have them consistent across both the go and the python files. They should be relatively self explanatory, but just incase the size_of_image/frame_dimension is the number of pixels in the x and y direction of the final gif, the num_frames/number_frames is the number of frames and max_iterations is the total number of iterations allowed to check for convergence.

In the python file:

#This is somewhere near the top
size_of_image = 1024
num_frames = 30
max_iterations = 1000

In the go file:

//This is somewhere near the start of main
frame_dimension := float64(1024)
number_frames := float64(30)
max_iterations := 1000

Note: more than 100 total frames is not currently supported. It would not be difficult to change so that it does work however, but it may take a while for the program to complete.

The only parameters that you can customize exclusively in the go file are as follows. They influence the point on which you are zooming in on, and the dimensions of the starting frame. This also tangentially means that if you have a large biggest_coord_offset, then you will be starting from a very far out zoom. That means that you will need to have the gif be going for a bigger frame count in order to zoom in enough. You can also modify the zoom rate further down to change the change in image size between the images.

In the go file:

//This is in the function callingAllPoints
//Change the .9 to something
x_offset := float64(zoom_factor * math.Pow(.9, i-1))


//The following is in main:
starting_coordinate := -0.7453 + 0.1127i //This is an interesting point to zoom in on
biggest_coord_offset := float64(.0013) //This is an interesting starting size too

The only parameters that you can exclusively modify in the python file is the frames per second of the final gif! Modify it in the following function call at the end of the file. In the python file:

write_gif(gif, 'test.gif', fps=10) #change fps as desired

Note: Do not attempt to use large image sizes and a large amount of frames when generating the gif on the first try, as array2gif can only handle so large of a data set.

Code Structure

In this section there will be a general outline of what the code is doing, and how it ends up generating a gif of the mandlebrot set. The code is well commented, and for more information on specific details see said comments.

Go code

The go code is responsible for generating the data and the .txt files. In each .txt file there will be entries in the following format : real_coordinate, imaginary_coordinate, num_iterations. Each frame will then have a .txt file with all the coordinates in the frame assosciated, however within each file all of the points will be out of order. This will come into play when considering what the python file has to do, but is non-important for now.

The way in which the go code goes about generating all of this data, and writing it to files is as follows. Keep in mind that information about every single point to be investigated for all of the frames is stored in a large map, and this is how we get some of our efficiency increases. I.e. if a point is in multiple frames, we only have it's calculations preformed once and then that information shared across frames through the map:

  1. For every frame in the gif (calling all points function)
    1. For every coordinate in the frame
      1. Temporarily store the information about the point stored in the map
      2. Add to that data point the information that it is needed at the current frame
      3. Add it back to the map with the updated information
  2. Spawn off go routines to calculate if the points diverge, or converge, and not the number of iterations taken to diverge, and go routine(s) to collect the calculated threads (spinUpCollector and feedCalculators functions)
    1. Note that each thread deals with different points and pulls new points from a channel so no overlap occurs. They also feed their output back into a channel so that the collector can deal with each point sequentially. Each thread is simply calculating the number of iterations before a point diverges, or if it converges.
    2. The collector gathers the calculated points and adds them to the appropriate frames in the gif. There is only one collector as of now, but it may be worth spinning up more to get more efficiency. This would be thread safe since each thread is only dealing with one point at a time.
  3. Write the final set of frames to different files once all the calculations and collections are completed.

This is the relatively optimized section of the code, and generating the data about the frames takes little to no time. The number of go routines spawned could be optimized however, and this should be tested if you desire better results.

Python code

This code is responsible for getting the data generated by the go code and compiling it into gif form. Note that when executing this code over large data sets, performance is somewhat sub-optimal. That being said, it still works, but considering we do not know exactly how gifs work, and also what the library array2gif is doing, it is a bit difficult for us to attempt to optimize it. As a result we have something that is working, but potentially not the most efficiently. There are also some other limitations as discussed previously. In general though this is what the python code is doing:

  1. For every frame file
    1. Read in the frame data
      1. Append every point and the number of iterations to a dictionary
      2. Sort the dictionary by the key (this inherently sorts the data by the real component in ascending order, then imaginary component in ascending order)
      3. Iterate over this sorted data and append to the information about the frame sequentially
        1. To decide what color the point should be, feed the number of iterations into the function iterations_to_RGB
    2. Append it to the final gif
  2. Build the gif (using array2gif)

Again note that this code takes a substantial amount of time to run, and it may not be clear if it is going to break right until the last minute. It is suggested that you slowly build up how big of a data set you feed into it so as to not waste too much time troubleshooting. Also it might be reasonable to add loading bars to this process to get a better indication on how long is remaining.

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

License

MIT