So I've got this awesome NVIDIA RTX A6000, this is a GPU that has 48 GB of RAM. Thank you NVIDIA!
Let's do something really cool with it. Let's generate a 32K, you heard me right, not an 8K,
not a 4K, not even a 16K, but a 32k image with it, or even video. The same code works just fine for
that, of a Mandelbrot. I like Mandelbrot, because it works quite well with the GPU. It's all linear
algebra, and it's a very very simple equation. It's and the code to produce this is quite short,
but it runs on this technology, on the GPU using complex numbers really really fast,
and it's also a high-precision operation, because as you zoom in on the Mandelbrot,
like is very common in a lot of videos exploring it, you need extremely precise numbers. So
we'll also take a look at 64-bit floating points and see see what that looks like.
We'll essentially have a 128-bit complex number loaded on the GPU and lots and lots of pixels.
So here is the code necessary for this, and this is what I really like about the Mandelbrot
set. It's really very simple, when you get right down to it.
There's the code to actually render it. This has all of the nice colorations that you typically see
in a Mandelbrot. I did not invent this particular way of coloring. It it's kind of a blue color.
You'll see all kinds of different ways to color Mandelbrot. This part alone can get really
complex. The part that we're really dealing with here, though is actually plotting the
Mandelbrot and essentially what you can think of a Mandelbrot is a two-dimensional plane.
just like a computer screen that has a bunch of pixels. Each of those pixels has a coordinate,
but it's not the coordinate on your screen. It's on a cartesian plane. X is essentially
the real numbers and Y is essentially the imaginary numbers. So together they become complex
numbers. You don't even have to really think of the complex numbers. Just think of it as X and Y.
So let's look at how to actually call it. First, before we actually look at how to implement it
this is the function that I wrote up there. It accepts several render sizes. This is the size of
the image that you're going to actually create. I have 32K, that's about the biggest size that
I can generate. I don't have a 32K monitor. I have a 4K. That's as high as I go, that I
can actually see it depending on which article you read the human eye. Some claim it does not do much
more than 4K. I don't tend to believe that, but because I've seen 4K and 8K next to each other.
Some claim that around 32K is the top. So who knows I'm not a biologist or an optometrist, so
I don't know the logical extent of that. Recording at this does have its uses and I'll show you that
in a moment. The render size that I chose here is just HD for the notebook. Generating this massive
thing into the notebook doesn't do a lot for you. Then there's a center that the origin is actually
right around here, I believe. You typically shift a little bit otherwise this part will get cut
off of the classic Mandelbrot. And the reason you saw that sine and cosine up in the render is just
so that we can cycle through the colors a bunch of times. Otherwise it would just be a gradient.
It would be blue all the way out to black and just once. And you can zoom in on this thing infinitely
so the colors have got to cycle otherwise you're not going to get much coloration. Zoom is how far
you're zoomed in you can really zoom in almost infinitely on this this is why you need the
64-bit floats and not the 32-bit. I'll show you what happens when you do 32-bit in a moment. It's
kind of disturbing. The cycles you can think of the cycles as almost the quality of it. But
we'll see mathematically exactly what a cycle is in a moment. 200 gives you a pretty good thing,
I've done even 2,000 or 20,000 this is where the GPU rocks. Plotting this kind of thing on the CPU,
pop this into CoLab and try it with and without and you'll see what I mean.
It's an order of magnitude faster on the GPU. And then when you run it, you're seeing this.
Let's go ahead and take a look at a few examples. Let's look at what it looks like when we render
one at 32K. So I'm basically going to render this to a "test.png" file. Let me pop that open
in Photoshop, so you can see it here. You'll want to be careful of the image editor that you use for
this kind of thing. If we look at the canvas size you can see this thing is gigantic. Where this
is neat is I can have this large image and I can zoom in on it. And I can keep zooming in further
and further and further and it's all pre-rendered because it's, this is all just in photoshop now.
We'll eventually reach the limit. See there there we're down to the pixel. So even a 32K image,
when you're dealing with something like Mandelbrot is not that big. This shows how you can render one
of the zoom videos that they do of Mandelbrot, and if you want to start experimenting with zooming
your own Mandelbrot's this code is actually a great starting point, because you can basically
just modify it. And that rendering area is where you're going to really express your creativity,
but this is all I'm really doing I am going to render through 3,000 images and I am going to
plot it over and over and over again. I rendered it at HD, because that's the
clip that I'm going to throw into here and show you in a second. And then
this is the starting point now, I had to search around and find. I wanted a good starting point
that if you zoom in on it you're not just going to zoom into black, because
in some of these. There are YouTube videos that do an hour zoom on this. I can't imagine
the precision that they were getting into on calculating the numbers for these. This point
lasts pretty long. Eventually it diverges off into blackness. And then the zoom I'm just
gradually decreasing the zoom. I start out at 1 and I just gradually gradually zoom in
99% each of the way through that. I save all the individual frames and then I'm going to use
ffmpeg to put it together. Then when you run it it looks like this. You can see this awesome zoom,
as I am going deeper and deeper and deeper into the Mandelbrot. This is not a particularly long
zoom compared to some of those, that they render on the Internet, but if you wanted to go further
you could certainly do that. You would just adjust that 3,000 value that I had in there and
and go deeper. The trick with going deeper though is you're going to converge into the
into the black zone and not get any more images. Because you're not going to find some hidden piece
of Mandelbrot out in the black area of the image. So let's go up to the function here
all of this right here is all that you need to generate a Mandelbrot. So what this is doing
is, I've got to get it to a cartesian plane, so I need the x's and the y's. Now this thing is
centered at zero. So it's not like a computer screen where zero is the upper corner it's a
cartesian plane. I'm going to need to go negative and positive on the x-axis the x-axis is the real
numbers. If you're dealing in complex numbers and then the y-axis is the imaginary numbers, up and
down all centered at zero. So, what I'm going to do is be passed in the render size the point that
it's centered on. So the point that would be in the middle of my render. Zero is is common, but
you change that because you want to zoom in on an interesting part of it not the middle of it. Zero
is black on this render, and that's not going to give you much information. And then zoom
takes you closer and closer in and then cycles we'll get that to a minute that's essentially
the quality. So the first thing we're going to do is divide zoom by the render size, in x. So f
is essentially the pixel size. This is the mapping between the pixels on that cartesian plane.
Which can be infinitely small, so it's not like a pixel on a computer screen, which is just
a fixed size, but we pick a size on the cartesian plane of the Mandelbrot, and we essentially,
that's the smallest unit that we're putting onto the map. Now when we zoom in even further,
that breaks up as well. So f and it's square we're not mapping rectangular non-square pixels.
Here we have to calculate the real start the real end. The imaginary start and the imaginary end,
so this is on the coordinates of the cartesian plane that is used to map it. So you real that's x
so real start that's the start in x and the start and y that's going to be centered about the the
center pixel that you have the center that you're passing in and in imaginary that's up and down.
And all I'm doing here is basically just taking the render size divided by two,
since we have the center point and scaling them all by the f, which is the size of our pixel
in the Mandelbrot world. And then I set up ranges. This is the range between the real
and the imaginary the x and the y. Notice I'm using float 64. And this is interesting because
in neural networks you don't you just don't need 64-bit precision, because for one thing,
the optimization algorithms are going to optimize out the imprecision, just just like anything else.
In Mandelbrot that'll kill you. So if I change these to 32 and I render a video. Take a look at
this now. I'll warn you this this might be one of those seizure type videos because it gets a
bit shaky. So it's it reminds me of Blair Witch Project a little bit. If you've ever seen that. It
but it's kind of like watching a spaceship crash. So here here it runs
and this you can see it's it's like watching a crash. It gets closer,
closer, closer, starts to get shaky and then boom lines... it's it's done.
You've got to have 64-bit to get that fluid motion that I was showing you earlier.
So these are two ranges those are essentially your two axes. You've got the the the real and
the imaginary. Then you have this mesh grid and notice I'm doing this all in the GPU,
because I'm using TensorFlow to do this. By the way, would you like to see the same thing done
in PyTorch? It would not be that that different, or would you like to see it done in native CUDA.
I could certainly do that as well that would be below even PyTorch and Keras itself. So mesh grid
what this is doing is it's taking those two axes and it's creating, think of it like a
checker board, with grids. You're giving it a step value which is the which is the f.
So you're stepping through each of those and you're stepping through the other axis,
and together that's a checker board and each point on that checkerboard is a coordinate essentially
an x and a y or a real and imaginary part. That's why it looks like a complex number, so this mesh
grid is a grid of it's really 3D two by two and then each one has has its own dimension as well.
We get a grid constant the grid constant that is the the same value. That this mesh grid really
even started at and notice we are converting it to a complex number. So we've got the, we keep a
constant of it just where it started and then we keep the current values that are going to change.
Initially these two are the same, but we're going to modify them as we go. And then counts becomes
very important because we're going to essentially run the same equation over and over and over again
on the same number and see if see if the number stays in a nice orbit. Think of something like
a sine or a cosine it's just it's just going up and it's not converging out to infinity
we keep the count of how long these have not just shot off to infinity and that's really
what you're looking at on. A Mandelbrot is just how many iterations can you get through a very
simple equation before the resulting value, that keeps getting fed back into that same equation,
just converges off into infinity. And how many counts that is what is determining
your pixel colors Mandelbrot helper is where all that magic actually happens. So Mandelbrot helper,
I separated this off as a separate function because you might want to make it a tf_function.
I didn't find enough complexity in here that that was particularly helping me to run faster,
so i don't have the tf_function enabled here. We're going to loop for the number of cycles so
this loop is fixed and we're going to create a temporary variable now this is a matrix of
complex numbers. And this is this function that you see right here this equation is the
heart of the Mandelbrot process. We're taking the current value squared, so multiplied it by itself,
added to the starting value that constant that we had we're just constantly looping through this,
for all of the cycles. So we keep taking the current values and squaring them. Normally
the square something goes up up up up but because these are fractional values it's not necessarily
just shooting off to infinity they they tend to orbit in a lot of cases. And the ones that the
pixels the form the tight orbits and don't go off to infinity those are the values that are
black in the rendering of the Mandelbrot that we're going to see in a moment. So diverge means
that it's shot off to infinity to tell that we're going to take the absolute value of this
this complex complex meaning using using complex numbers. Not complex as in complicated this is
actually a very simple equation. So long as it's not greater than 4 because this is a this is the
cartesian plane we're just basically drawing a 4 rectangle around it and if you look at.
If you look at the non zoomed in Mandelbrot it's like drawing a box
around it, and that's essentially the the four that you have there. So if for each of these
pixels we're looking at as we do that square over and over and over again for the complex
numbers does it just sort of orbit. If it just orbits it's black, if it shoots off to infinity
then it's going to have one of these colors and how long it takes it to shoot off to infinity
that's where the color comes from. There's also other values that you can deal with in
Mandelbrot's, like how tight is the orbit there's there's a lot of information that the more
colorful complex plots make use of that in this simple example. I'm basically throwing away and
then we take the values that we have calculated from from temp and we just assign it back into
current values and that's that loop that you have going on where you're constantly multiplying them.
If you want to see some good visuals of how Mandelbrot is actually calculated you can
definitely see those on a lot of different videos that they have on YouTube for these.
The point of this video is we're loading this all into this gigantic GPU
that NVIDIA is kind enough to give to me and showing that we can really render a
very high resolution even the high res zoom that I show you here did not take
because it's at 4k did not take all that much time really to render. Thank you for
watching this video and if you're interested in artificial intelligence rendering and all these
kind of things please subscribe to my channel and please give the video a like thank you very much.