Generating the Mandelbrot Set
scionofbytes.me
scionofbytes.me
As a high school student I was fascinated and implemented it on any machine I had access to. I remember being bored in math class and coding it on my TI-81 calculator. That took a while to render in black and white but it did work!
Great memories. Thanks for posting.
I killed the battery by setting the bailout to a ridiculously high value and forgetting about it overnight.
I still have the calculator, but the memory was cleared at one point. I have been tempted to recreate the Mandelbrot program to show my kids, but I can't bring myself to devote that much of my life to it again.
I still remember discovering Render To Disk and realizing I could save stuff out and open it up in Rainbow Paint... which has been lost in the sands of internet.
In a recent contract I had very little to do for a few weeks and access to a ~9000 core server cluster and a workload-distribution framework...
Unfortunately as it was on client time and equipment I couldn't take it with me and it lives on only as a piece of sample code attached to the client's distributed processing framework!
But damn - drawing 38400 x 21600 resolution sets in record time was fun :)
I did one on my Casio FX-7000G. The fun of programming under real[1] constraints!
[1] Yes, yes, it wasn't punch cards or core memory or using a hammer to magnetise rocks...
Ahhhhhh analog.
BTW: .ps (PostScript) is a fun little stack based language, similar to JVM. Document is simply the result of the program execution. You can program it by hand, though I do not recommend anything large.
[1] https://www.mostlymaths.net/2008/12/quick-postscript-program...
[2] https://gist.github.com/rberenguel/9a5239034cafdeaabdf665444...
[3] https://gist.github.com/rberenguel/38f5106fee2a795be45d6a335...
Edit: having a blog with posts from 10 years ago you want to reference is painful... Broken links, grammar errors, unclear writing, typos...
Multi-megabyte memories and fast RISC processors with FPUs were not uncommon.
Some quick DDG-ing around suggests that there isn't a great solution for arbitrary-precision GPU numbers, and that trying to force it on to GPUs either loses all the speed advantages it has over CPUs or even just loses to CPUs. Corrections solicited.
(Remember... GPUs aren't "faster" than CPUs. They're different. There are things GPUs are much faster than CPUs, but there's also plenty of tasks... indeed, it's most of them that we actually perform... where CPUs are faster. You can get differentials of 100x-1000x in either direction. That's why we use CPUs.)
I found this version [0] that uses perturbation theory to get deep zoom on the GPU. I haven't seen it before and haven't looked into it, but it seems neat.
They are more parallel for very specific things, most of which go flying out the window when you try to use arbitrary precision floats. GPUs are not parallel arrays of CPUs; they're parallel arrays of what are very stupid and unoptimized execution units from a CPU's point of view. (The "stupid and unoptimized" is to some extent a feature; you can control them deeply, unlike the high performance CPUs, which are to the point where they're not just second-guessing you, they're second-guessing their second guesses. But they don't have a lot of caches, they don't reorder things themselves, etc.)
From the linked project: "Using the perturbation method, we can calculate only one point with an arbitrary precision in CPU and restore the rest of the picture with float precision in GPU."
Now that's where the big wins really are. I Have A Dream (which will never happen) of writing a programming language that makes it easier to mix CPU and GPU execution in some sensible manner.
And the Mandelbrot set is one of those things, at least when you're not doing arbitrary precision which the article isn't doing anyway.
> I Have A Dream (which will never happen) of writing a programming language that makes it easier to mix CPU and GPU execution in some sensible manner.
You need to take care so the synchronization isn't eating your performance wins though. I know you wrote "in a sensible manner", so you probably already thought of that. I don't know if another programming language is what the world needs, but there are libraries available to at least ease the pain.
I recently found Vulkan compute for people. I haven't tried it yet, maybe you'll find it interesting.
Like I said, I haven't gotten too far into it, but one of the other ideas that keeps coming to mind is to have some sort of cost model in the language, because one of my other I Have A Dream elements of a new language is the ability to make assertions about blocks of code like "this code is inlined" or (in a GC'ed language) "this variable is stack allocated", or in the case of crypto code, "this code is optimized only to constant-time operations". These assertions wouldn't make the thing they are asserting happen, because experience over the decades shows that that doesn't work (witness the way that inline pragmas have gotten consistently weaker over the years to the point that I believe they're no-ops in many environments), but they'd be signs to the compiler to say back to the user that if the constraint is violated, here's a clear message explaining why.
The idea could be adapted to something like "this block of code only copies the data to the GPU once" or some variant of that claim (a given byte only copied once).
I think a similar thing could be used for some interesting network/cloud transparency, too, where the language permits wiring up function calls/messages/whatever transparently, but you can use labeling to make compile-time (or possibly start-up time) assertions about how expensive the result is going to be. I've tossed around the idea of making a function call not necessarily be the fundamental primitive, but maybe have something weaker than that be the cross-module default, like an Erlang message pass. One of the reasons networks are such a pain to work with in conventional programming languages is the mismatch between function calls and network operations; it would be interesting to expand on Erlang and see if we might be able to address that via weakening the function call across modules.
Anyhow, like I said, I'll never get to any of this, but I do sort of feel like there's a surprising amount of room for a new language out there that doesn't get explored very well because people keep remaking Python or something like D over and over again, just with slightly different spelling.
Also, potentially pyCUDA and other things that google autocompletes when you type "pyCUDA vs".
None of them are exactly what you're talking about, of course.
[0] http://graphics.stanford.edu/papers/halide-cacm18/halide-cac...
I was very impressed by Halide but I'm not involved and have no idea how widely it is used, sorry. I could imagine that a lot of potential users enjoy tweaking GPU and SIMD code enough, or have enough confidence in their results, that they don't give it a good look.
for a brief and beautiful moment there were CPUs designed in a way they could be used as GPUs and yet be programmed as CPUs. Intel called them Larrabee at first, then Xeon Phi.
Obviously for more zoom, you're still going to need more bits (and therefore different codepaths for different zoom levels).
Fancy approximations can probably be done to look at computation results from neighbouring pixels too. The number of bits to store the difference from an intermediate pixel is far smaller.
If it doesn’t work in your browser because of the camera input, try replacing the iChannel0 input with one of the built-in videos. Firefox seems to work best for me to use the Camera input, and turning your head into MandelHeads is pretty hilarious...
> Let’s just say that for our purposes we can think of complex numbers existing on a line perpindicular to our regular number line. If you take a piece of graph paper and mark a line from left to right, and then another line that crosses that line at 90 degrees, from top to bottom, you can imagine that the horizontal line represents the real number line, and the vertical line represents the complex number line.
The vertical line is the imaginary number line. 2 is as much a complex number as 3i is (2 + 0i vs 0 + 3i).
Mandelbrot argued that commonly shapes in nature are fractals. So, we could go to a mountain range, find a lake, and then suspect that the surface is a 2D fractal.
Well, as bizarre as the Mandelbrot set is, actually it is a closed set in the usual topology for the plane. Then there is a theorem that there is a function from the plane to the line that is 0 on the Mandelbrot set, strictly positive otherwise, and infinitely differentiable. Similarly there is a function that is 0 on the boundary of that set, negative in the interior, and positive otherwise.
So, in general, with the Mandelbrot set just an example, these results hold for any closed set in the plane. And the result generalizes to dimension 1 and all finite dimensions greater than 2. So, a set is closed if and only it it is the level set of an infinitely differentiable function 0 on the boundary of the set, negative in the interior, and positive otherwise.
This result can be used to show that in the constraint qualifications for the Karush-Kuhn-Tucker conditions, the (i) Zangwill and (ii) Kuhn-Tucker constraint qualifications are independent. For a while that fact was an outstanding unanswered question about constraint qualifications.
Somewhere in one of the papers of Arrow, Hurwicz, and Uzawa on mathematical economics there is a similar question about constraint qualifications asked but not answered that is easily answered with an example based on these results about infinitely differentiable functions and closed sets.
Back to the mountains, we can argue that the surface of the land, above, at the boundary, and on the bottom of the lake can be infinitely differentiable while the lake is the Mandelbrot set or any closed set.
Bizarre stuff!
def N(eps):
c = -0.75 + eps*1j
n = 0
z = 0
while abs(z) < 2:
z = z*z + c
n += 1
return n
[N(eps) for eps in [0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001]]There's a neat video explaining it here: https://www.youtube.com/watch?v=d0vY0CKYhPY
$ pypy3 man.py
[33, 315, 3143, 31417, 314160, 3141593, 31415927, 7853981629]I believe you are just running into the limits of double precision arithmetic; indeed log_2((10^8)^2) > 53.
Apparently I attempted this in Haskell 10 years ago: https://gist.github.com/jrockway/291074. I seem to recall adding things to that GTK image builder the slowest part by far. I bet things are better 10 years later though.
I became obsessed with IFS (iterated function systems) a looooong time ago. Take a look at the colage theorem and try to make your own ;-)
Cheers
Some modern fractal things that I appreciate:
Example IFS generator: http://sirxemic.github.io/ifs-animator/ Mandelbulb 3D (amazing): http://www.mandelbulb.com
If anyone wants to start with something simple, try and write a Sierpinski Gasket generator. Very very simple, yet infinitely complicated.
That can do a load of different types (Pickover Biomorphs, Newton-Raphson approximations, burning ships, Mandel-drops etc). Always loved drawing fractals. One day I'll get around to implementing Ray-marching and draw the mandelbulb!
...I remember back when I was in high school making a generator for this in BASIC on an Apple IIe, running 16 colors in the "double high res mode" (needed the 80-column card for it - only machine that had it) - and having it run for hours just to generate one image.
Slow...sigh.
- trying to scroll the page using arrow keys or Page Up/Down keys takes several seconds between key press and response
- when dragging the scroll bar manually, the new scrolled-in area is blank white for several seconds before finally filling in with content
- even right-click, or click-drag to select text, takes a few seconds to respond
I tried disabling javascript on the page, but it makes no difference, so it's not some runaway script. I'm guessing that maybe there are some very high resolution Mandelbrot images that are being scaled in the browser?
I've wanted for a long time to get around to generating the Mandelbrot as a 3D height map (Fractint could do that from a static viewpoint), and make it explorable. Ideally where you can change size to explore it all...
The results were nothing close to real-time but the speed difference was astronomical. With a bit of tricking the compiler to produce good code you could get a screenful in a few minutes. There were good Mandelbrot zoomers for Amiga written in assembler that you could, maybe barely but still, call real-time renderers. Of course, I added panning and zooming in mine as well eventhough the performance at the time was more akin to "real-long-time" rather than "real-time".
The number of optimisations available are mind-boggling, especially for real-time renderers which only need to care about correctness in the final image -- which often won't be rendered at all because the user has moved elsewhere or zoomed deeper before getting there.
So, rendering as you iterate is one thing you can do: as soon as the image has enough detail, it's "finished" for all practical purposes and either the user moves around elsewhere or leaves the renderer to finish the final iterations with hopefully only minor updates on the actual screen.
Also, because the set is the (typically) black area the details for the nice color shades are quicker to compute because those pixels run away sooner. On the other hand, the shaded areas are really not where the genuine details are so you're left with a lot of ways to precalculate estimated values for these pixels when you're certain you're not too close to the set. A real-time renderer can also save work in only computing new pixels revealed by panning and reusing pixels that have only moved but still on the screen. To some extent it's also possible to quickly extrapolate temporary pixel colors after panning, then try to lock down where there might be pixels that belong to the set.
Of course, the usual, more mechanical inner-loop optimisations apply as well. This becomes mandatory at deeper zoom levels where you raise the threshold for discarding pixels, and you actually have to run lots of iterations for nearly all pixels.
I just love these applications where a seemingly simple algorithms lends itself to endless amount of performance optimisations. Especially with fractals the limits are literally... unlimited.
https://en.wikipedia.org/wiki/Bifurcation_diagram
And the book Chaos, by James Gleick
Oh, and The Cuckoo's Egg, by Clifford Stoll
Take a point called Z in the complex plane,
let z[1] be z^2+C...
let z[2] be z[1]^2+c...
let z[3] be z[2]^2+c
And so on.
ffplay -f lavfi -i mandelbrot