{"id":21766365,"url":"https://github.com/drilonaliu/parallel-mandelbrot-set","last_synced_at":"2026-04-12T07:34:32.915Z","repository":{"id":252255798,"uuid":"837246729","full_name":"drilonaliu/Parallel-Mandelbrot-Set","owner":"drilonaliu","description":"GPU-accelerated Mandelbrot Set generation with CUDA and OpenGL interoperability.","archived":false,"fork":false,"pushed_at":"2024-08-08T15:14:24.000Z","size":3000,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-26T02:18:21.660Z","etag":null,"topics":["cuda","fractals","gpu","mandelbrot-fractal","parallel-programming"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/drilonaliu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-02T14:21:55.000Z","updated_at":"2024-10-10T22:09:36.000Z","dependencies_parsed_at":"2024-08-08T17:29:51.232Z","dependency_job_id":null,"html_url":"https://github.com/drilonaliu/Parallel-Mandelbrot-Set","commit_stats":null,"previous_names":["drilonaliu/parallel-mandelbrot-set"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drilonaliu%2FParallel-Mandelbrot-Set","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drilonaliu%2FParallel-Mandelbrot-Set/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drilonaliu%2FParallel-Mandelbrot-Set/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drilonaliu%2FParallel-Mandelbrot-Set/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/drilonaliu","download_url":"https://codeload.github.com/drilonaliu/Parallel-Mandelbrot-Set/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244741964,"owners_count":20502382,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cuda","fractals","gpu","mandelbrot-fractal","parallel-programming"],"created_at":"2024-11-26T13:16:58.151Z","updated_at":"2026-04-12T07:34:32.883Z","avatar_url":"https://github.com/drilonaliu.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mandelbrot Set\nThe Mandelbrot Set is a fractal named after Benoit B. Mandelbrot, defined by the recursive function $z_{n+1} = z_n^2 + c$, where $z$ and $c$ are complex numbers. \nStarting with $z_0 = 0$, the complex number $c$ belongs to the Mandelbrot set if the sequence $z_n$ is bounded as $n \\to \\infty$.\nFor example, the point $c = 1$ is not a member of the Mandelbrot set because for $c = 1$, the sequence $0, 1, 2, 5, 26$ grows without bound. The point $c = -1$ is a member of the Mandelbrot set because the sequence $0, -1, 0, -1, 0, \\ldots$ is bounded. \nTheorem 1 will help us in visualizing the fractal.\n\n**Theorem 1.** The complex number $c$ belongs to the Mandelbrot set if and only if $|z_n| \\leq 2$ for every $n \\geq 1$.\n\n\nEach pixel of the image represents a point in the coordinate system, and for that point, we test if $|z_n| \\leq 2$ after $n_\\text{max}$ iterations.\nIf we color the points that belong to the set in black and the points that do not belong to the set in white, we will get the image shown below.\n\n\u003cp\u003e\n   \u003cimg src=\"images/mandelbrot_1.png\"\u003e\n\u003c/p\u003e\n\n## Maping the pixels into the coordinate system\n\n\n\u003cp\u003e\n   \u003cimg src=\"images/image_to_coordinate.png\"\u003e\n\u003c/p\u003e\n\nTo map pixels into the coordinate system, we use the line equation. \nLet the image $I$ have dimensions $w \\times h$. Let $(x_1, y_1)$ and $(x_2, y_2)$ be the starting and ending points of the coordinate system into which the image should be mapped, as shown in the figure above.\nEach pixel $(i, j)$ should represent a point $(x, y)$ in the coordinate system. For example, the pixel $(0, 0)$ is mapped to $(x_1, y_1)$.\n\nThe interval $[0, w]$ should be mapped to $[x_1, x_2]$, and the interval $[0, h]$ should be mapped to $[y_1, y_2]$. We define the mappings $f_x$ and $f_y$ for these intervals.\n\n$$f_r : [0, w] \\rightarrow [x_1, x_2]$$\n$$f_c : [0, h] \\rightarrow [y_1, y_2]$$\n\n\nFor the coordinates $x$, we have:\n\n$$ f_x(0) = x_1 $$\n\n$$f_x(w) = x_2 $$\n\nThe linear transformation for the coordinates $x$ can be obtained as the equation of the line passing through the points $(0, x_1)$ and $(w, x_2)$.\n\n$$\\frac{i - 0}{w - 0} = \\frac{x - x_1}{x_2 - x_1}$$\n\nSolving for $x$ we get\n\n$$\\frac{i - 0}{w - 0} = \\frac{x - x_1}{x_2 - x_1} \\implies \\frac{i}{w} = \\frac{x - x_1}{x_2 - x_1} \\implies x - x_1 = \\frac{i}{w} (x_2 - x_1) \\implies x = \\frac{i}{w} (x_2 - x_1) + x_1$$\n\nIn an analogous way we get\n\n$$y = \\frac{j}{w} (y_2 - y_1) + y_1$$\n\nFinally, the pixel $(i, j)$ is mapped to the point $(x, y)$ in the coordinate system using the following formulas:\n\n$$x = \\frac{i}{w} (x_2 - x_1) + x_1$$\n$$y = \\frac{j}{h} (y_2 - y_1) + y_1$$\n\n## Coloring the Mandelbrot\n\nThe coloring of the Mandelbrot set involves assigning colors to points in the complex plane based on how quickly the sequence $z_n$ tends to infinity. \nWe need a number $t$ from 0 to 1 to represent this speed. This value is then converted to the HSV color space and then to RGB.\n\n\nThere are many ways to calculate the number $t$. Let $n_\\text{max}$ be the maximum number of iterations. \nLet $n$ be the iteration number at which $z_n$ either escapes from the Mandelbrot set ($n \u003c n_\\text{max}$) or remains within the set and does not escape ($n = n_\\text{max}$).\nThe most basic way to calculate the number $t$ is:\n\n$$t = \\frac{n}{n_{\\text{max}}}$$\n\nAnother method is to use the formula known as the normalized iteration count.\n\n$$s = n + 1 - \\frac{\\log(\\log(|z_n|))}{\\log(2)}$$\n\nThe value $s$ is normalized with respect to the maximum number of iterations:\n\n$$t = \\frac{s}{n_{\\text{max}}}$$\n\nLet the color triplet in the HSV color space be $(H_{HSV}, S_{HSV}, V_{HSV})$, where $H_{HSV}, S_{HSV}$, and $V_{HSV} \\in [0,1]$.\nIn our case, the hue value will be $H = t$, the saturation $S_{HSV} = 1$, and the value $V_{HSV} = 1$. \nThe conversion of this triplet to the RGB color space is done as follows [@hsv_rgb]:\n\n$$\\begin{aligned}\nH' \u0026= (6 \\cdot H_{HSV}) \\mod 6  \\\\\nc_1 \u0026= \\lfloor H' \\rfloor, \\quad c_2 = H' - c_1 \\\\\nx \u0026= (1 - S_{HSV}) \\cdot v \\\\\ny \u0026= (1 - (S_{HSV} \\cdot c_2)) \\cdot V_{HSV} \\\\\nz \u0026= (1 - (S_{HSV} \\cdot (1 - c_2))) \\cdot V_{HSV}\n\\end{aligned}$$\n\nBased on the value of $c_1$, the normalized values $R', G', B' \\in [0,1]$ are calculated from $v = V_{HSV}$, $x$, $y$, and $z$ as follows:\n\n$$(R', G', B') = \n\\begin{cases}\n    (v, z, x) \u0026 \\text{if } c_1 = 0 \\\\\n    (y, v, x) \u0026 \\text{if } c_1 = 1 \\\\\n    (x, v, z) \u0026 \\text{if } c_1 = 2 \\\\\n    (x, y, v) \u0026 \\text{if } c_1 = 3 \\\\\n    (z, x, v) \u0026 \\text{if } c_1 = 4 \\\\\n    (v, x, y) \u0026 \\text{if } c_1 = 5.\n\\end{cases}$$\n\n\\\nFinally, the RGB values are scaled to integers in the range $[0, N - 1]$ (usually $N = 256$).\n\n$$R = \\min(\\text{round}(N \\cdot R'), N - 1),$$\n$$G = \\min(\\text{round}(N \\cdot G'), N - 1),$$\n$$B = \\min(\\text{round}(N \\cdot B'), N - 1).$$\n\nAfter zooming into the fractal, the colors appear as shown in the figure.\n\n\u003cdiv\u003e\n  \u003cp\u003e\n    \u003cimg src=\"images/mandelbrot_3.png\" width=\"200\" height=\"200\" alt=\"Tree 4\"\u003e\n    \u003cimg src=\"images/mandelbrot_4.png\" width=\"200\" height=\"200\" alt=\"Tree 1\"\u003e\n    \u003cimg src=\"images/mandelbrot_5.png\" width=\"200\" height=\"200\" alt=\"Tree 2\"\u003e\n    \u003cimg src=\"images/mandelbrot_6.png\" width=\"200\" height=\"200\" alt=\"Tree 3\"\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\n## Kernel\n\nThe following kernel generates an image of the Mandelbrot set. Each thread corresponds to a pixel in this image.\nInitially, the complex coordinates of each pixel are calculated based on the thread index.\nThen the iteration begins using the Mandelbrot formula to determine if a pixel escapes from the set within a certain number of iterations. \nRGB colors are assigned based on the iteration number at which $z_n$ terminates, and the colors are stored in an output array for visualization.\n\n```\n       __global__ void kernel(uchar4* ptr, double zoomfactor, double shiftX, double shiftY, int iterations, int width, int height) {\n\n        int i = threadIdx.x + blockIdx.x * blockDim.x;\n        int j = threadIdx.y + blockIdx.y * blockDim.y;\n            \n        int offset = i + j * blockDim.x * gridDim.x;\n            \n        double aspectRatio = (1.0 * width) / (1.0 * height);\n        double startIntervalX = (-2) * zoomfactor * aspectRatio;\n        double endIntervalX = 1 * zoomfactor * aspectRatio;\n        double startIntervalY = 1.5 * zoomfactor;\n        double endIntervalY = -1.5 * zoomfactor;\n            \n        //Each pixel represents a point in the Oxy coordinate system. These points are \n        double x = (endIntervalX - startIntervalX) * i / (width * 1.0) + startIntervalX + shiftX;\n        double y = (endIntervalY - startIntervalY) * j / (height * 1.0) + startIntervalY + shiftY;\n            \n        double c_x = x;\n        double c_y = y;\n        double zX = 0, zY = 0, a = 0, b = 0;\n        int max_iteration = iterations;\n        int iteration;\n        double squaredSums;\n        for (iteration = 0; iteration \u003c max_iteration; iteration++) {\n            double zX_squared = zX * zX;\n            double zY_squared = zY * zY;\n            a = zX_squared - zY_squared + c_x;\n            b = 2 * zX * zY + c_y;\n            zX = a;\n            zY = b;\n            squaredSums = zX_squared + zY_squared;\n            if (squaredSums \u003e 4) {\n                break;\n            }\n        }\n        int R = 0, G = 0, B = 0;\n        if (iteration \u003c max_iteration) {\n        setRGB(squaredSums, iteration, max_iteration, R, G, B);\n        }\n        ptr[offset].x = R;\n        ptr[offset].y = G;\n        ptr[offset].z = B;\n        ptr[offset].w = 0;\n    } \n```\n\n## Comparisions\n\nTable below compares the excecution time in microseconds of the fractal between the sequential version and the parallel one in a resolution 1920x1080.\n\n\n| Iterations | Sequential | CUDA   |\n|------------|------------|--------|\n| 100        | 733,630    | 9,400  |\n| 130        | 843,132    | 10,436 |\n| 160        | 937,728    | 11,135 |\n| 190        | 1,072,441  | 11,930 |\n| 210        | 1,133,325  | 12,447 |\n| 255        | 1,266,945  | 15,498 |\n| 445        | 1,935,680  | 14,152 |\n| 610        | 2,493,841  | 16,123 |\n| 775        | 3,098,080  | 19,263 |\n| 1,065      | 4,057,208  | 30,757 |\n| 2,300      | 8,331,929  | 63,800 |\n\n\n\n\u003cp\u003e\n   \u003cimg src=\"images/mandelbrot_2.png\"\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrilonaliu%2Fparallel-mandelbrot-set","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdrilonaliu%2Fparallel-mandelbrot-set","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrilonaliu%2Fparallel-mandelbrot-set/lists"}