{"id":4047,"date":"2017-06-27T10:24:58","date_gmt":"2017-06-27T10:24:58","guid":{"rendered":"http:\/\/stratio.com\/?p=4047"},"modified":"2023-09-20T13:33:04","modified_gmt":"2023-09-20T13:33:04","slug":"sparkart-part-one","status":"publish","type":"post","link":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/","title":{"rendered":"SparkArt! Part One: Exploring the depths of Mandelbrot Set with Spark"},"content":{"rendered":"<p style=\"text-align: justify;\">On March the 26th 2012, James Cameron and\u00a0his submarine craft, Deepsea Challenger, explored the depths of the ocean down to 11km under sea level at\u00a011.329903\u00b0N 142.199305\u00b0E, an infinitesimal point on the surface of the Earth&#8217;s vast Oceans. Could you imagine how incredible it would be to\u00a0have thousands of &#8220;Deep Challengers&#8221; reaching the bottom of our planet in parallel? What a map we would get!<!--more--><\/p>\n<table>\n<tbody>\n<tr>\n<th>\n<p><figure style=\"width: 300px\" class=\"wp-caption alignnone\"><img decoding=\"async\" src=\"http:\/\/i.imgur.com\/OK3hTwY.png\" width=\"300\" \/><figcaption class=\"wp-caption-text\">James Cameron&#8217;s trip<\/figcaption><\/figure><\/th>\n<th>\n<p><figure style=\"width: 300px\" class=\"wp-caption alignnone\"><img decoding=\"async\" src=\"http:\/\/i.imgur.com\/AFz4qVY.png\" width=\"300\" \/><figcaption class=\"wp-caption-text\">Distributed sea exploration<\/figcaption><\/figure><\/th>\n<\/tr>\n<\/tbody>\n<\/table>\n<p style=\"text-align: justify;\">Throughout the lines of this post we&#8217;ll see how such a feat is possible, just let me replace the\u00a0Earth&#8217;s Oceans with\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Mandelbrot_set\" target=\"_blank\" rel=\"noopener noreferrer\">The Mandelbrot Set<\/a>\u00a0and\u00a0thousands of &#8220;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Deepsea_Challenger\" target=\"_blank\" rel=\"noopener noreferrer\">Deepsea Challengers<\/a>&#8221;\u00a0by\u00a0Hundreds of distributed execution units. Yes! We will see how it is possible to draw\u00a0<strong>HUGE<\/strong>\u00a02D representations of this beautiful fractal using the distributed computational power which only\u00a0Apache Spark can provide.<\/p>\n<h2>What is this fractal about?<\/h2>\n<p style=\"text-align: justify;\">As the reader surely knows, fractals are self-repetitive physical or abstract structures such as geometric figures generated\u00a0by repeating their dominant\u00a0features as they get zoomed in on.<\/p>\n<p style=\"text-align: justify;\">The Mandelbrot set is a set of complex numbers whose 2D representation shows the self-replication property of fractals. This representation consist of\u00a0drawing the points belonging to the complex set in a 2D plane using a given color, let&#8217;s use\u00a0<strong>black<\/strong>, and the points not on the set with a different color, we&#8217;ll see which one (or rather which color palette) soon.<\/p>\n<h2>Drawing figures with complex numbers<\/h2>\n<p style=\"text-align: justify;\">2D complex number representation is as simple as using the real part as the value for the x-axis value and the imaginary part as a value in the y-axis. e.g: The complex value\u00a0<strong>-1+2i<\/strong>\u00a0could be represented as:<\/p>\n<figure style=\"width: 200px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/i.imgur.com\/v2uY5L2.png\" width=\"200\" height=\"205\" \/><figcaption class=\"wp-caption-text\">-1+2i complex number 2D representation<\/figcaption><\/figure>\n<p>Thus a set of infinite points cover a surface, potentially forming a geometric figure, e.g:<\/p>\n<figure style=\"width: 232px\" class=\"wp-caption aligncenter center\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/i.imgur.com\/Povgwdg.png\" width=\"232\" height=\"275\" \/><figcaption class=\"wp-caption-text\">A random complex set representation<\/figcaption><\/figure>\n<p style=\"text-align: justify;\">As we can&#8217;t handle an infinite number of points, the resolution of the surface would be given by the number of points in the set, the more you have, the less porous the painted surface is.<\/p>\n<p style=\"text-align: justify;\">So, yes, it is possible to draw things by just providing a set of complex numbers. And The Mandelbrot set is a complex set, this has been known for ages by now. See this\u00a0example of a representation created by a primitive computer in\u00a01989:<\/p>\n<figure style=\"width: 597px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/i.imgur.com\/9jtpnVW.png\" width=\"597\" height=\"764\" \/><figcaption class=\"wp-caption-text\">The Emperor&#8217;s new mind &#8211; Pg 98 &#8211; Roger Penrose 1989<\/figcaption><\/figure>\n<h2>So it is done, why Spark?<\/h2>\n<p style=\"text-align: justify;\">There you go, \u00a0a single 1989 computer was already able to draw a representation of the set so that&#8217;s all, end of the post. I hope you enjoyed it&#8230;<\/p>\n<p style=\"text-align: justify;\">Just kidding! The problem with the figure above is that it is so coarse , so zoomed out that you don&#8217;t see anything beyond a kind of bug, maybe some kind of\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Rorschach_test\" target=\"_blank\" rel=\"noopener noreferrer\">Rorschach test<\/a>\u00a0, nothing of apparent interest.\u00a0The ancient computer is to blame in this case. it couldn&#8217;t provide a large enough set to represent the nuances of the details hidden behind this black blob. Lets jump 30 years in the future and see what Spark can provide given the same mathematical description:<\/p>\n<figure style=\"width: 482px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/i.imgur.com\/kpr66RO.png\" width=\"482\" height=\"366\" \/><figcaption class=\"wp-caption-text\">The Mandelbrot set representation using Apache Spark<\/figcaption><\/figure>\n<p>Hey! those strange branches aren&#8217;t a anything else than the figure itself!<\/p>\n<figure style=\"width: 800px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/i.imgur.com\/6KC7VcH.png\" width=\"800\" height=\"600\" \/><figcaption class=\"wp-caption-text\">Better resolution. more insight. The blur was hiding\u00a0features.<\/figcaption><\/figure>\n<p style=\"text-align: justify;\">This is an improvement for sure! But, believe me, you could have generated this &#8220;high resolution&#8221; image with a cheap PC in less than a minute. We haven&#8217;t discussed the details of the algorithm yet but, I promise you, there is no need to use\u00a0<strong>the power of distributed computation\u00a0<\/strong>to create this representation.<\/p>\n<h2>An infinite universe to explore<\/h2>\n<p style=\"text-align: justify;\">The colourful representation shown above is yet small just 1MP (Mega Pixel). However, the points in the complex set are infinite, meaning that you could keep zooming in and calculating whether or not the, each time more infinitesimal, points of the 2D plane fall inside or\u00a0outside the set. To paint them in black, if they fall inside, or in another color if not. No, there is nothing wrong with your screen\u00a0nor with your perception (at least regarding this): All of a sudden we have colors! Let&#8217;s ignore them and\u00a0<strong>keep in mind that the black dots are those representing complex numbers within the Mandelbrot Set.<\/strong><\/p>\n<p style=\"text-align: justify;\">A good analogy here are black holes, everything within their event horizon is not there to see: It is black, the information around it is what we can actually see in a colorful fashion.<\/p>\n<p style=\"text-align: justify;\">So, before explaining how, let&#8217;s take a look at the result of using\u00a0<strong>distributed computation with\u00a0Apache Spark to compute a\u00a0<span style=\"text-decoration: underline;\"><a href=\"http:\/\/orionsword.no-ip.org\/demos\/fractals\/pyramid\" target=\"_blank\" rel=\"noopener noreferrer\">ONE TERAPIXEL REPRESENTATION OF THE MANDELBROT SET<\/a>.<\/span><\/strong><\/p>\n<p style=\"text-align: justify;\">Don<b>&#8216;<\/b>t be shy, enter and explore yourself. In any case, you can find an example in the following video:<\/p>\n<p style=\"text-align: justify;\">Is in these extremely huge cases where more computational power is needed, where we can harness the potential of distributed systems to reach levels of detail hardly imaginable\u00a0for a single computer.<\/p>\n<h2>Colors, unveiling the algorithm<\/h2>\n<p>So far, we&#8217;ve just discussed what this is all about and seen the results of this experiment, now it&#8217;s time to start digging right into how this huge, but limited at the same time, snapshot of an infinite universe can be generated.<\/p>\n<p style=\"text-align: justify;\">We can\u00a0ignore distribution, parallelization and fancy computation accelerators for now and center our attention in the classical sequential algorithm. As we have discussed by now, the idea is to map complex values onto an area on a 2D plane, let&#8217;s refer to that rectangular region as our canvas. To map these complex numbers, first we have to find them.<\/p>\n<p style=\"text-align: justify;\">For that, we will sample the 2D plane, that is an interval of complex numbers, and check which elements of the sample fall into the set. A relatively simple algorithm to test whether or not a complex number is part of the set\u00a0is the\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Mandelbrot_set#Escape_time_algorithm\" target=\"_blank\" rel=\"noopener noreferrer\">Escape Time Algorithm<\/a>\u00a0. Without getting into the mathematical details (which are better explained at Wikipedia\u00a0than by\u00a0any attempt of me to bring them to this TLDR post) the idea is:<\/p>\n<ol>\n<li style=\"text-align: justify;\">Take\u00a0<strong>z = 0<\/strong>\u00a0as starting value.<\/li>\n<li style=\"text-align: justify;\">Take as\u00a0<strong>c<\/strong>\u00a0the complex value whose belonging to the set we want to check.<\/li>\n<li style=\"text-align: justify;\">Compute the result of the (tail) recursive function:\n<pre class=\"lang:scala decode:true\">\/\/ Not the actual implementation, a quick draft to illustrate the idea.\ndef f(z: Complex, c: Complex): Boolean = {\n    if(modulus(z) &gt; 4.0) false \/* ESCAPED THE RECURSION!\n                                  c wasn't part of the set *\/\n    else f(z*z + c)\n}<\/pre>\n<\/li>\n<\/ol>\n<p style=\"text-align: justify;\">If we run the snippet from above.\u00a0We have two options: Keep our computer running until the end of the universe or change\u00a0<strong>f\u00a0<\/strong>to stop iterating at a certain number of iterations. The points in the set are guaranteed to not make\u00a0<strong>z^2+c\u00a0<\/strong>diverge therefore, by definition, \u00a0<span class=\"lang:scala decode:true crayon-inline\">f(Complex.Zero, cInput)<\/span>\u00a0should indefinitely call to itself.\u00a0\u00a0Thus, the function should be changed to include some kind of limit:<\/p>\n<pre class=\"lang:scala decode:true\">\/\/ Not the actual implementation, a quick draft to give you an idea.\ndef f(z: Complex, c: Complex)(maxIterations: Int): Boolean = {\n    if(modulus(z) &gt; 4.0) false \/* ESCAPED THE RECURSION!\n                                  c wasn't part of the set *\/\n    else if(maxIterations == 0) true\n    else f(z*z + c)(maxIterations-1)\n}<\/pre>\n<p style=\"text-align: justify;\">And this way, we&#8217;ve transformed the algorithm into an approximation algorithm where the accuracy\u00a0of its results increase as the initial limit of iterations increases. That is, the probability of getting false positives (wrongly acknowledging values as belonging to the set) increases as\u00a0\u00a0<span class=\"lang:scala decode:true crayon-inline\">maxIterations<\/span>\u00a0gets reduced, sadly for us, the bigger this limit is, the slower the algorithm is.<\/p>\n<p style=\"text-align: justify;\">Having to constraint the number of iterations is a toll we&#8217;ve to pay if we want to get our results in a reasonable time. Nevertheless, it has a silver lining, a cool feature which bestows our representation with colourful contours. What if, due to the points not belonging to the set and therefore escaping before reaching the limit, we returned the number of iterations it took to diverge?<\/p>\n<pre class=\"lang:scala decode:true\">\/\/ Not the actual implementation, a quick draft to make an idea.\ndef f(z: Complex, c: Complex)(\n    maxIterations: Int,\n    currentIteration: Int = 0\n): Option[Int] = {\n    if(modulus(z) &gt; 4.0) Some(currentIteration)\n                               \/* ESCAPED THE RECURSION!\n                                  c wasn't part of the set, that is now\n                                  represented by returning Some(noIterations)\n                                *\/\n    else if(currentIteraton == maxIterations) None \/* Reached the limit, not interested \n                                                      in the number of iterations as \n                                                      the function didn't escape *\/\n    else f(z*z + c)(maxIterations, currentIteration+1)\n}\n\nval explorationResult: Option[Int] = f(Complex.Zero, Complex(x, y))(1000)\nval inMandelbrotSet: Boolean = explorationResult.isEmpty\n\n\/* The number of iterations for escaped values can be used\n   to give color to the complement set of Mandelbrot set *\/\n\nval palette: Vector[Color] = ???\n\nval color = explorationResult map { n =&gt; \n    palette(n % palette.size)\n} getOrElse Black\n<\/pre>\n<p style=\"text-align: justify;\">This algorithm produces nice results for small\u00a0samples or small limits to the number of iterations, you can find a single threaded implementation which can be run in your very own browser at\u00a0<a href=\"https:\/\/github.com\/pfcoperez\/mandelbrot_scalajs\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/github.com\/pfcoperez\/mandelbrot_scalajs<\/a>, it is in\u00a0<a href=\"https:\/\/github.com\/pfcoperez\/mandelbrot_spark\/blob\/master\/math\/src\/main\/scala\/org\/pfcoperez\/iterativegen\/MandelbrotSet.scala#L3\" target=\"_blank\" rel=\"noopener noreferrer\">this experiment repository as well<\/a>, implemented without notions of parallelization. We are about to discover that it can be used in a parallel computation just as it is, no changes required:<\/p>\n<pre class=\"lang:scala decode:true\" title=\"Sequential implementation, can be used to scale horizontally \">package org.pfcoperez.iterativegen\n\nobject MandelbrotSet {\n\n  \/** A single iteration of the numeric method\n    *  O(1)\n    *\n    * @return `None` if the point escapes the set,\n    *         `Some(new complex state)` otherwise.\n    *\/\n  def iteration(\n                 zeroth: (Double, Double)\n               )(\n                 xy: (Double, Double)\n               ): Option[(Double, Double)] = {\n    val (x, y) = xy\n    val (x0, y0) = zeroth\n\n    if(x*x + y*y &gt;= 4.0) None\n    else Some((x*x - y*y + x0, 2.0*x*y+y0))\n  }\n\n  \/** Several iterations, it can be used to approximate\n    *  Mandelbrot's set. The more iterations,the better\n    *  the approximation is.\n    *\n    * O(nIterations)\n    *\n    * @zeroth Start point, e.g: (x,y) \\in ([-2.5, 1], [-1,1]) area.\n    * @nIterations Max depth to be check in the recursion.\n    * @prevSt Starting point in the expliration\n    * @return The state after `nIterations` or condition escape.\n    *\n    *\/\n  def numericExploration(\n                          zeroth: (Double, Double),\n                          nIterations: Int,\n                          prevSt: Option[((Double, Double), Int)] = None\n                        ): (Option[(Double, Double)], Int) = {\n\n    val (prevPoint, prevIterations) = prevSt.getOrElse((0.0, 0.0) -&gt; 0)\n\n    def numericExploration(current: (Double, Double), it: Int): (\n      Option[(Double, Double)], Int\n      ) =\n      if(it == nIterations) (Some(current), prevIterations+it)\n      else iteration(zeroth)(current) match {\n        case Some(p) =&gt; numericExploration(p, it+1)\n        case _ =&gt; (None, prevIterations+it)\n      }\n\n    numericExploration(prevPoint, 0)\n\n  }\n\n}<\/pre>\n<h2>Explorers Galore!<\/h2>\n<p style=\"text-align: justify;\">Coming back to James Cameron&#8217;s adventures, given the coordinates of a point on the\u00a0Earth surface, he explored that point by going deeper and deeper with his submarine. This is exactly what we&#8217;re doing with our\u00a0<strong>Escape Time Algorithm<\/strong>, we set a point in our\u00a0<strong>canvas\u00a0<\/strong>and then we descend as deep as the maximum number of iterations determines:<\/p>\n<ul>\n<li style=\"text-align: justify;\">We might make a remarkable discovery before reaching the bottom, that is, the point escaped the set before stopping because we reached the maximum number of iterations.<\/li>\n<li style=\"text-align: justify;\">We might as well reach the bottom empty-handed\u00a0and assume the point belongs to the set. Remember, this is an approximation algorithm.<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" src=\"http:\/\/i.imgur.com\/2Wqqykc.png\" width=\"1102\" height=\"400\" \/><\/p>\n<p style=\"text-align: justify;\">Now, consider having a fleet of explorers\u00a0<del>diving<\/del>\u00a0running in parallel. If our goal is to cover the maximum area in the most dense way possible, getting a complete representation would very much help, wouldn&#8217;t it?<\/p>\n<p style=\"text-align: justify;\">A dive into the depths is a run of the escape algorithm,<strong>\u00a0we don&#8217;t need to make any effort in trying to parallelize the exploration for a single point, we just need to apply it to each element of our sample in a parallel and, even better, distributed way.<\/strong>\u00a0Spark not only makes this possible but also makes child&#8217;s play of it.<\/p>\n<h2 style=\"text-align: justify;\">The sample is a\u00a0<del>mess<\/del>\u00a0mesh<\/h2>\n<ul>\n<li style=\"text-align: justify;\"><strong>Our input<\/strong>\u00a0is our sample.<\/li>\n<li style=\"text-align: justify;\"><strong>Our sample<\/strong>, a set of 2D points, that is (x,y) values to determine whether or not they represent a complex number belonging to the Mandelbrot Set. That is,\u00a0<strong>a mesh of points covering a 2D region.<\/strong><br \/>\n<figure style=\"width: 230px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" src=\"http:\/\/i.imgur.com\/vkoiLuU.png\" width=\"230\" \/><figcaption class=\"wp-caption-text\">The square with x in [0,2] and y in [1,3] is a region sampled by the blue dots {(0,1), (1, 1), (2, 1), (0,2), (1, 2), (2, 2), (0,3), (1, 3), (2, 3)} This set of dots is what we call a mesh.<\/figcaption><\/figure><\/li>\n<li>And<strong>\u00a0our output,\u00a0<\/strong>coloured images: In black for those points in the set, following a color palette for those that escaped it.<\/li>\n<\/ul>\n<p style=\"text-align: justify;\">Generating that kind of mesh in Spark is easy. It is just a collection of\u00a0<strong>(x,y)\u00a0<\/strong>tuples:<\/p>\n<ol>\n<li>Describe\u00a0all values for\u00a0<strong>x:\u00a0<\/strong>\n<pre class=\"lang:scala decode:true\">val x = sc.range(0,3,1) \/\/sc &lt;- SparkContext<\/pre>\n<\/li>\n<li>Describe\u00a0all values for\u00a0<strong>y<\/strong>:\n<pre class=\"lang:scala decode:true\">val y = sc.range(1,4,1)<\/pre>\n<\/li>\n<li>Build a mesh combining both ranges with cartesian product, conveniently provided by Spark as an RDD operation:\n<pre class=\"lang:scala decode:true\">val mesh = x cartesian y<\/pre>\n<\/li>\n<\/ol>\n<p>Come on! give it a try:<\/p>\n<pre class=\"lang:scala decode:true\">scala&gt; mesh.collect foreach (println)\n\n(0,1)\n(0,2)\n(0,3)\n(1,1)\n(1,2)\n(1,3)\n(2,1)\n(2,2)\n(2,3)\n<\/pre>\n<p style=\"text-align: justify;\">As our output will be an image, we will be\u00a0generating our sample as a mesh of pixels by just applying some scaling transformations, it is possible to map those pixels into the complex region we want to explore. If you&#8217;re curious about how that was done you can take a look at\u00a0<a href=\"https:\/\/github.com\/pfcoperez\/mandelbrot_spark\/blob\/master\/math\/src\/main\/scala\/org\/pfcoperez\/geometry\/Primitives2D.scala\" target=\"_blank\" rel=\"noopener noreferrer\">Primitives2D.scala<\/a>\u00a0object. Scaling is performed\u00a0by taking into account your working context (pixel points or real points). Depending on our selected 2D area, those pixel points will be automatically transformed into real points representing complex values:<\/p>\n<pre class=\"lang:scala decode:true\">...\n...\n...\n\ncase class PixelFrame(upperLeft: Pixel, bottomRight: Pixel) extends Frame[Long]\n  case class RealFrame(upperLeft: Point, bottomRight: Point) extends Frame[Double]\n\n  case class Scale(realFrame: RealFrame, pixelFrame: PixelFrame)\n\n  object Implicits {\n\n    implicit def tuple2pixel(t: (Long, Long)): Pixel = {\n      val (x, y) = t\n      Pixel(x, y)\n    }\n\n    implicit def tuple2point(t: (Double, Double)): Point = {\n      val (x, y) = t\n      Point(x, y)\n    }\n\n    implicit def real2pixel(p: Point)(implicit scale: Scale): Pixel = {\n      import scale._\n      import p._\n\n      require(realFrame contains p, s\"$p out of $realFrame\")\n\n      val (pxMin, pxMax) = pixelFrame.xRange\n      val (pyMin, pyMax) = pixelFrame.yRange\n\n      val (xMin, xMax) = realFrame.xRange\n      val (yMin, yMax) = realFrame.yRange\n\n      val px = pxMin + ((pxMax-pxMin)*(x-xMin)\/(xMax-xMin)).toLong\n      val py = pyMin + ((pyMax-pyMin)*(y-yMin)\/(yMax-yMin)).toLong\n\n      Pixel(px, py)\n    }\n\n    implicit def pixel2real(p: Pixel)(implicit scale: Scale): Point = {\n      import scale._\n      import p.{x =&gt; px, y =&gt; py}\n\n      require(pixelFrame contains p, s\"$p out of $pixelFrame\")\n\n      val (pxMin, pxMax) = pixelFrame.xRange\n      val (pyMin, pyMax) = pixelFrame.yRange\n\n      val (xMin, xMax) = realFrame.xRange\n      val (yMin, yMax) = realFrame.yRange\n\n      val x = xMin + (xMax-xMin)*((px.toDouble-pxMin)\/(pxMax-pxMin))\n      val y = yMin + (yMax-yMin)*((py.toDouble-pyMin)\/(pyMax-pyMin))\n\n      Point(x, y)\n    }\n\n  }\n\n...\n...\n...<\/pre>\n<p style=\"text-align: justify;\">We could&#8217;ve implemented the &#8220;scale&#8221; concept as\u00a0<a href=\"http:\/\/blog.plover.com\/prog\/burritos.html\" target=\"_blank\" rel=\"noopener noreferrer\">a burrito, I mean, a Monad<\/a>. I didn&#8217;t consider that at the time I was hurrying to build \u00a0the experiment as fast as I could, quite an error as\u00a0<a href=\"https:\/\/github.com\/typelevel\/cats\/blob\/master\/core\/src\/main\/scala\/cats\/functor\/Invariant.scala\" target=\"_blank\" rel=\"noopener noreferrer\">Invariant<\/a>\u00a0could have removed all that unnecessary boiler plate.<\/p>\n<p style=\"text-align: justify;\">Mapped out &#8220;virtual world&#8221;:\u00a0<span style=\"color: #339966;\"><strong>DONE<\/strong><\/span><\/p>\n<h2>Got a map and an army of vehicles, wonders\u00a0await<\/h2>\n<p style=\"text-align: justify;\">At this point we are ready to get a first approximation of the set. We&#8217;ve got a collection of points, a sample, in a distributed collection, a\u00a0<span class=\"lang:scala decode:true crayon-inline \">RDD[(Long, Long)]<\/span>\u00a0it only takes filtering over it using the escape algorithm shown above to know which points should be black, \u00a0which ones are part of the set:<\/p>\n<pre class=\"lang:scala decode:true \">val (w, h) = dimensions \/\/ Resolution, in pixels, of the sample\n\n\/\/sc &lt;- SparkContext\nval x = sc.range(0,w,1)\nval y = sc.range(0,h,1)\n\nval mesh = x cartesian y\n\nimplicit val scale: Scale = Scale( \/\/Select complex interval [-2.5-i, 1+i]\n                                   RealFrame(-2.5 -&gt; -1.0, 1.0 -&gt; 1.0),\n                                   PixelFrame(0L -&gt; 0L, (w-1L, h-1L))\n                                 )\n\nval pointsInMandelbrotSet = mesh filter { (x, y) =&gt;\n  val point: Point = Pixel(x, y) \/\/Automatically applies the scale defined above\n  val (st, _) = numericExploration(point.tuple, maxIterations)\n  st.nonEmpty \/* If the end state of the exploration is None,\n                it is because it escaped the set, there is no more state to continue iterating. *\/\n}<\/pre>\n<p style=\"text-align: justify;\">As easy as that, we distributed the computation in less than twenty lines.<\/p>\n<h2 style=\"text-align: justify;\">To be continued&#8230;<\/h2>\n<p style=\"text-align: justify;\">If you&#8217;re familiar with how Spark works you might be thinking\u00a0<em>&#8220;Oh boy, that&#8217;s an awful lot of shuffling!&#8221;\u00a0<\/em>and you would be right. If not, you&#8217;ll see why that is the case and, in any case, you&#8217;ll discover how to avoid that.<\/p>\n<p style=\"text-align: justify;\">See you soon to discuss that and other optimizations, as well as a thorough description of how these distributed collections can become images.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>On March the 26th 2012, James Cameron and\u00a0his submarine craft, Deepsea Challenger, explored the depths of the ocean down to 11km under sea level at\u00a011.329903\u00b0N 142.199305\u00b0E, an infinitesimal point on the surface of the Earth&#8217;s vast Oceans.<\/p>\n","protected":false},"author":1,"featured_media":4048,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[686],"tags":[19,85],"ppma_author":[795],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v22.9 (Yoast SEO v22.9) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>SparkArt! Part One: Exploring the depths of Mandelbrot Set with Spark<\/title>\n<meta name=\"description\" content=\"Draw HUGE 2D representations of the Mandelbrot Set using the distributed computational power which only Apache Spark can provide.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"SparkArt! Part One: Exploring the depths of Mandelbrot Set with Spark\" \/>\n<meta property=\"og:description\" content=\"Draw HUGE 2D representations of the Mandelbrot Set using the distributed computational power which only Apache Spark can provide.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/\" \/>\n<meta property=\"og:site_name\" content=\"Stratio\" \/>\n<meta property=\"article:published_time\" content=\"2017-06-27T10:24:58+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-09-20T13:33:04+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.stratio.com\/blog\/wp-content\/uploads\/2017\/06\/fractal.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"730\" \/>\n\t<meta property=\"og:image:height\" content=\"312\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Stratio\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@stratiobd\" \/>\n<meta name=\"twitter:site\" content=\"@stratiobd\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Stratio\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/\"},\"author\":{\"name\":\"Stratio\",\"@id\":\"https:\/\/www.stratio.com\/blog\/#\/schema\/person\/d0377b199cd052b17e15c9ba44c45ab7\"},\"headline\":\"SparkArt! Part One: Exploring the depths of Mandelbrot Set with Spark\",\"datePublished\":\"2017-06-27T10:24:58+00:00\",\"dateModified\":\"2023-09-20T13:33:04+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/\"},\"wordCount\":2034,\"publisher\":{\"@id\":\"https:\/\/www.stratio.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.stratio.com\/blog\/wp-content\/uploads\/2017\/06\/fractal.jpg\",\"keywords\":[\"Big Data\",\"spark\"],\"articleSection\":[\"Product\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/\",\"url\":\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/\",\"name\":\"SparkArt! Part One: Exploring the depths of Mandelbrot Set with Spark\",\"isPartOf\":{\"@id\":\"https:\/\/www.stratio.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.stratio.com\/blog\/wp-content\/uploads\/2017\/06\/fractal.jpg\",\"datePublished\":\"2017-06-27T10:24:58+00:00\",\"dateModified\":\"2023-09-20T13:33:04+00:00\",\"description\":\"Draw HUGE 2D representations of the Mandelbrot Set using the distributed computational power which only Apache Spark can provide.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#primaryimage\",\"url\":\"https:\/\/www.stratio.com\/blog\/wp-content\/uploads\/2017\/06\/fractal.jpg\",\"contentUrl\":\"https:\/\/www.stratio.com\/blog\/wp-content\/uploads\/2017\/06\/fractal.jpg\",\"width\":730,\"height\":312},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.stratio.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"SparkArt! Part One: Exploring the depths of Mandelbrot Set with Spark\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.stratio.com\/blog\/#website\",\"url\":\"https:\/\/www.stratio.com\/blog\/\",\"name\":\"Stratio Blog\",\"description\":\"Corporate blog\",\"publisher\":{\"@id\":\"https:\/\/www.stratio.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.stratio.com\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.stratio.com\/blog\/#organization\",\"name\":\"Stratio\",\"url\":\"https:\/\/www.stratio.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.stratio.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/stratio.com\/blog\/wp-content\/uploads\/2020\/06\/stratio-web-logo-1.png\",\"contentUrl\":\"https:\/\/stratio.com\/blog\/wp-content\/uploads\/2020\/06\/stratio-web-logo-1.png\",\"width\":260,\"height\":55,\"caption\":\"Stratio\"},\"image\":{\"@id\":\"https:\/\/www.stratio.com\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/stratiobd\",\"https:\/\/es.linkedin.com\/company\/stratiobd\",\"https:\/\/www.youtube.com\/c\/StratioBD\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.stratio.com\/blog\/#\/schema\/person\/d0377b199cd052b17e15c9ba44c45ab7\",\"name\":\"Stratio\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.stratio.com\/blog\/#\/schema\/person\/image\/bb38888f58c2bb664646155f78ae6ccc\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e3387ad00609f34a56d6796400eb8191?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e3387ad00609f34a56d6796400eb8191?s=96&d=mm&r=g\",\"caption\":\"Stratio\"},\"description\":\"Stratio guides businesses on their journey through complete #DigitalTransformation with #BigData and #AI. Stratio works worldwide for large companies and multinationals in the sectors of banking, insurance, healthcare, telco, retail, energy and media.\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"SparkArt! Part One: Exploring the depths of Mandelbrot Set with Spark","description":"Draw HUGE 2D representations of the Mandelbrot Set using the distributed computational power which only Apache Spark can provide.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/","og_locale":"en_US","og_type":"article","og_title":"SparkArt! Part One: Exploring the depths of Mandelbrot Set with Spark","og_description":"Draw HUGE 2D representations of the Mandelbrot Set using the distributed computational power which only Apache Spark can provide.","og_url":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/","og_site_name":"Stratio","article_published_time":"2017-06-27T10:24:58+00:00","article_modified_time":"2023-09-20T13:33:04+00:00","og_image":[{"width":730,"height":312,"url":"https:\/\/www.stratio.com\/blog\/wp-content\/uploads\/2017\/06\/fractal.jpg","type":"image\/jpeg"}],"author":"Stratio","twitter_card":"summary_large_image","twitter_creator":"@stratiobd","twitter_site":"@stratiobd","twitter_misc":{"Written by":"Stratio","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#article","isPartOf":{"@id":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/"},"author":{"name":"Stratio","@id":"https:\/\/www.stratio.com\/blog\/#\/schema\/person\/d0377b199cd052b17e15c9ba44c45ab7"},"headline":"SparkArt! Part One: Exploring the depths of Mandelbrot Set with Spark","datePublished":"2017-06-27T10:24:58+00:00","dateModified":"2023-09-20T13:33:04+00:00","mainEntityOfPage":{"@id":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/"},"wordCount":2034,"publisher":{"@id":"https:\/\/www.stratio.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#primaryimage"},"thumbnailUrl":"https:\/\/www.stratio.com\/blog\/wp-content\/uploads\/2017\/06\/fractal.jpg","keywords":["Big Data","spark"],"articleSection":["Product"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/","url":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/","name":"SparkArt! Part One: Exploring the depths of Mandelbrot Set with Spark","isPartOf":{"@id":"https:\/\/www.stratio.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#primaryimage"},"image":{"@id":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#primaryimage"},"thumbnailUrl":"https:\/\/www.stratio.com\/blog\/wp-content\/uploads\/2017\/06\/fractal.jpg","datePublished":"2017-06-27T10:24:58+00:00","dateModified":"2023-09-20T13:33:04+00:00","description":"Draw HUGE 2D representations of the Mandelbrot Set using the distributed computational power which only Apache Spark can provide.","breadcrumb":{"@id":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.stratio.com\/blog\/sparkart-part-one\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#primaryimage","url":"https:\/\/www.stratio.com\/blog\/wp-content\/uploads\/2017\/06\/fractal.jpg","contentUrl":"https:\/\/www.stratio.com\/blog\/wp-content\/uploads\/2017\/06\/fractal.jpg","width":730,"height":312},{"@type":"BreadcrumbList","@id":"https:\/\/www.stratio.com\/blog\/sparkart-part-one\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.stratio.com\/blog\/"},{"@type":"ListItem","position":2,"name":"SparkArt! Part One: Exploring the depths of Mandelbrot Set with Spark"}]},{"@type":"WebSite","@id":"https:\/\/www.stratio.com\/blog\/#website","url":"https:\/\/www.stratio.com\/blog\/","name":"Stratio Blog","description":"Corporate blog","publisher":{"@id":"https:\/\/www.stratio.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.stratio.com\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.stratio.com\/blog\/#organization","name":"Stratio","url":"https:\/\/www.stratio.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.stratio.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/stratio.com\/blog\/wp-content\/uploads\/2020\/06\/stratio-web-logo-1.png","contentUrl":"https:\/\/stratio.com\/blog\/wp-content\/uploads\/2020\/06\/stratio-web-logo-1.png","width":260,"height":55,"caption":"Stratio"},"image":{"@id":"https:\/\/www.stratio.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/stratiobd","https:\/\/es.linkedin.com\/company\/stratiobd","https:\/\/www.youtube.com\/c\/StratioBD"]},{"@type":"Person","@id":"https:\/\/www.stratio.com\/blog\/#\/schema\/person\/d0377b199cd052b17e15c9ba44c45ab7","name":"Stratio","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.stratio.com\/blog\/#\/schema\/person\/image\/bb38888f58c2bb664646155f78ae6ccc","url":"https:\/\/secure.gravatar.com\/avatar\/e3387ad00609f34a56d6796400eb8191?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e3387ad00609f34a56d6796400eb8191?s=96&d=mm&r=g","caption":"Stratio"},"description":"Stratio guides businesses on their journey through complete #DigitalTransformation with #BigData and #AI. Stratio works worldwide for large companies and multinationals in the sectors of banking, insurance, healthcare, telco, retail, energy and media."}]}},"authors":[{"term_id":795,"user_id":1,"is_guest":0,"slug":"stratioadmin","display_name":"Stratio","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/e3387ad00609f34a56d6796400eb8191?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.stratio.com\/blog\/wp-json\/wp\/v2\/posts\/4047"}],"collection":[{"href":"https:\/\/www.stratio.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.stratio.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.stratio.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.stratio.com\/blog\/wp-json\/wp\/v2\/comments?post=4047"}],"version-history":[{"count":32,"href":"https:\/\/www.stratio.com\/blog\/wp-json\/wp\/v2\/posts\/4047\/revisions"}],"predecessor-version":[{"id":13586,"href":"https:\/\/www.stratio.com\/blog\/wp-json\/wp\/v2\/posts\/4047\/revisions\/13586"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.stratio.com\/blog\/wp-json\/wp\/v2\/media\/4048"}],"wp:attachment":[{"href":"https:\/\/www.stratio.com\/blog\/wp-json\/wp\/v2\/media?parent=4047"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.stratio.com\/blog\/wp-json\/wp\/v2\/categories?post=4047"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.stratio.com\/blog\/wp-json\/wp\/v2\/tags?post=4047"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.stratio.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=4047"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}