MP 8: Mandelbrot Set Explorer
MP Update: pytest Fix
Thursday, Oct. 27: The initial release of MP8 ("v1") had a test suite that would not run successfully on perfectly correct implementations. The problems only impacted the test suite. On Thursday evening, we released an update ("v340") to fix the test suite.
If you started the MP on/after Thursday evening, you'll already have the latest version! You can know if you have the latest if the HTML page is marked as ("v340") in the header when visiting localhost:5000.
If you stared the MP BEFORE Thursday evening: See the Discord thread in #mp8 for details
Sorry about the messy test suite on release day. :(
Overview
In lecture, you learned about using Docker containers as a way to quickly build using new technologies and the usefulness of cloud-object storage. In this MP, you will:
- Explore a graphical rendering of the Mandelbrot set
- Utilize Docker to launch an S3-compatible object storage (
MinIO
) without installing it locally - Use the AWS
boto3
library for accessing your object storage - Build the middleware and backend of an application to explore the Mandelbrot set
Initial Files
In your CS 340 directory, merge the initial starting files with the following commands:
git fetch release
git merge release/mp8 -m "Merging initial files"
MP Overview Session
Ameya held the overview session on Thursday, Oct. 27 at 5pm. The video is linked [here].(https://mediaspace.illinois.edu/media/t/1_i462k16u)The slides can be found here.
Machine Problem
Part 1: The Mandelbrot Microservice
In mp8/mandelbrot_microservice
, you will find a fully complete Mandelbrot microservice. This microservice will render an image of the Mandelbrot set based on six parameters you provide:
- The Matplotlib
colormap
, - The center point (
real
,imag
), - The unit height (
height
), - The render dimensions (
dim
), and - The maximum iterations of the Mandelbrot set (
iter
)
Specifically, these parameters make up the endpoint of the image in the following format:
/mandelbrot/<colormap>/<real>:<imag>:<height>:<dim>:<iter>
For example, a very zoomed out view (height=3
) of the Mandelbrot set centered at real=0
+ imag=0
(0+0i) and rendered as a dim=256
(256px x 256px) using the (colormap=twilight_shifted
) and iter=64
iterations has the following endpoint:
/mandelbrot/twilight_shifted/0.0:0.0:3.0:512:64
By shifting the center to the right (real=0.4
) and zooming in (height=0.4
), we can see the detail in the boundary:
/mandelbrot/twilight_shifted/0.4:0.0:0.4:512:64
It’s your turn – run the mp8/mandelbrot_microservice
right now by going into the mp8/mandelbrot_microservice
directory, start the flask app (py -m flask run
), and continue the journey by viewing the following links:
- Shifting a bit more and zooming in even further:
- View more details by changing the iterations from 64 to
iter=128
: - Add even more detail by going to
iter=256
: - Zooming in by 10x to
height=0.001
: - Zooming in another 10x to
height=0.0001
: - Changing the colors to
colormap=inferno
:
You will notice that loading each image is VERY slow. The rest of this MP will have you work with exploring this API via a simple frontend that has been provided for you and caching the images that have been previously rendered using an object storage system using MinIO
.
There’s one last thing to do with the provided Mandelbrot microservice:
- Play around with the URL until you find a starting point you find visually interesting! The
height
should be less than or equal to0.01
, but every other parameter is up to you! :) - Make sure to record your starting point parameters. You’ll need them for Part 3!
Part 2: Launch a Docker Container using MinIO
MinIO
is an open-source object storage system that maintains a API compatibility with S3.
- Since
MinIO
maintains API compatibility with S3, you will use the AWS S3 libraryboto3
. - To connect to your local
MinIO
instance when usingboto3
, you’ll useendpoint_url="http://127.0.0.1:9000"
instead of the default value. Otherwise everything else works identically as S3.
To launch MinIO
, use the following docker run
command:
docker run -it --rm -p 9000:9000 -p 9090:9090 --name minio -e "MINIO_ROOT_USER=ROOTNAME" -e "MINIO_ROOT_PASSWORD=CHANGEME123" quay.io/minio/minio server /data --console-address :9090
- This command will set your
aws_access_key_id
toROOTNAME
- This command will set your
aws_secret_access_key
toCHANGEME123
Part 3: Complete the Mandelbrot Viewer Application
The final, and major part of this MP is the completion of the Mandelbrot Viewer application. Unlike other applications you have built in CS 340, this application is a stateful server. This means that your server must store the current location of your view of the Mandelbrot set internal to your app.
Modifying Application State
Your applications starts at the initial position, height, and all other parameters discussed you found while exploring the Mandelbrot microservice in Part 1. (This will be different for everyone.) The following routes must be implemented to then dynamically modify the position:
POST /moveUp
, moves the center of the image up by 25% of the currentheight
.POST /moveDown
, moves the center of the image down by 25% of the currentheight
.POST /moveLeft
, moves the center of the image to the left by 25% of the currentheight
.POST /moveRight
, moves the center of the image to the right by 25% of the currentheight
.POST /zoomIn
, modifies theheight
by a factor of1 / 1.4
.POST /zoomOut
, modifies theheight
by a factor of1.4
.POST /smallerImage
, modifies thedim
of the image by a factor of1 / 1.25
.POST /largerImage
, modifies thedim
of the image by a factor of1.25
.POST /moreIterations
, modifies theiter
of the image by a factor of2
.POST /lessIterations
, modifies theiter
of the image by a factor of1 / 2
.
Additionally, the colormap
is changed by the following route:
POST /changeColorMap
, changes thecolormap
to be equal to thecolormap
value in the JSON in the request’s body. For example, a sample request’s body toPOST /changeColorMap
is:{"colormap": "inferno"}
.
Rendering the Mandelbrot image: GET /mandelbrot
The GET /mandelbrot
route must render the an image of the Mandelbrot set based on the current state of your application. In addition to returning the generated Mandelbrot image:
- Your application must use the MinIO object storage to cache all generated images.
- If the image has been cached, this function must return the image from the object storage instead of using the Mandelbrot microservice.
- This means the ONLY time the Mandelbrot microservice is called is if the request has never been made before since the MinIO instance has launched.
Since the Mandelbrot microservice takes some amount of time to render the image, the cached image will be much quicker than the Mandelbrot microservice.
This route must send the image as an image/png
file (this is the same way the Mandelbrot microservice returns the image to your middleware) with a 200 response code. If any errors occur, return a non-200 response and make certain NOT to cache the error in your object storage. (It should be very rare for a non-200 response to occur.)
The boto3 guide on “Uploading files” may be helpful in working with MinIO.
Viewing All Stored Data: GET /storage
The GET /storage
route must return a JSON of every image stored as a array of entries. This must be done by querying your object storage (it must persist across restarting your application). Each entry in the JSON this route returns must be a dictionary with two key-value pairs:
key
, containing the unique route used for the Mandelbrot microservice route (ex:inferno/0.36:-0.09:0.0001:512:256
) .image
, containing a base64-encoded PNG image binary data with thedata:image/png;base64,
prefix.
For example, a response to GET /storage
may be:
[
{"key": "twilight/-0.7405:0.1314:0.015:164:512", "image": "data:image/png;base64,iVBORw0K..." },
{"key": "twilight/-0.7405:0.1314:0.0107:164:512", "image": "data:image/png;base64,iVBORw0K..." },
{"key": "twilight/-0.7405:0.1314:0.0077:164:512", "image": "data:image/png;base64,iVBORw0K..." },
...
]
The boto3 guide on “Downloading files” may be helpful in working with MinIO. The only other command you’ll need, which we leave to you to discover, is how to list all files in your bucket. :)
Setting Default Values: POST /resetTo
The POST /resetTo
must reset the parameters to the value specified by the JSON in the request’s body. The JSON will always have the following fields: colormap
, real
, imag
, height
, dim
, iter
.
For example, the body provided to /resetTo
may be:
{
"colormap": "twilight_shifted",
"real": 0.0,
"imag": 0.0,
"height": 3.0,
"dim": 512,
"iter": 64
}
Retrieving the Server’s State: GET /getState
Finally, the GET /getState
must return the current state as a JSON in the same format as /resetTo
.
For example, the response by /getState
may be:
{
"colormap": "cividis",
"dim": 256,
"height": 0.0003718688641637414,
"imag": 0.126129,
"iter": 512,
"real": -0.7434051354938358
}
Testing Your Code
Run your application and visit it using a web browser. This is the best way to test your code. You will need three different services running to fully run your application:
- Run the Mandelbrot microservice in
mp8/mandelbrot_microservice
, - Run the MinIO docker, and
- Run the Mandelbrot Viewer application in
mp8
and visit http://127.0.0.1:5000/
As usual, and automated test suite is provided by running python -m pytest
.
Submit
When you have completed your program, double-check that your server runs as expected. When you are ready, submit the code via the following git commands:
git add -u
git commit -m "MP submission"
git push