25

Is it possible to increase the performance of the DensityPlot?

For example, let's try to plot the following "flower"

f[x_, y_] := (x^2 + y^2) Exp[-x^2 - y^2] Sin[10 Sqrt[x^2 + y^2] + 10 ArcTan[x, y]]^4;

DensityPlot[f[x, y], {x, -3, 3}, {y, -3, 3}, PlotPoints -> 200, 
 MaxRecursion -> 3, ColorFunction -> Hue, PlotRange -> All, 
 ColorFunctionScaling -> False, ImageSize -> 600]

enter image description here

This toy example takes about 12 seconds on my laptop, eats about 1GB of RAM while plotting, and the 34MB result slows down the notebook.

The questions are:

  1. How to increase the speed?

  2. How to decrease the MaxMemoryUsed?

  3. How to decrease the size of the output?

ybeltukov
  • 43,673
  • 5
  • 108
  • 212

1 Answers1

23

Edit: tested with MMA 11.1, option Exclusions -> None added to recover the previous behavior.

I have found that my approach with textures has different applications:

Now I want to use it for the enhancement of the DensityPlot:

Options[fastDensityPlot] = Append[Options[DensityPlot], Subpoints -> 30];
SyntaxInformation[fastDensityPlot] = SyntaxInformation[DensityPlot];

fastDensityPlot[f_, {x_, xmin_, xmax_}, {y_, ymin_, ymax_}, opts : OptionsPattern[]] :=
 DensityPlot[f, {x, xmin, xmax}, {y, ymin, ymax},
       Evaluate@FilterRules[{opts}, Except@Subpoints]] // Normal // toTriangles // 
   texturize[Function[{#1, #2}, #3] & @@ {x, y, f},
        OptionValue[Subpoints], OptionValue[ColorFunction]]

Here Normal converts GraphicsComplex to separate polygons, toTriangles splits polygons to triangles, and texturize puts textures on every triangle (defined below), f is assumed to be Listable.

f[x_, y_] := (x^2 + y^2) Exp[-x^2 - y^2] Sin[10 Sqrt[x^2 + y^2] + 10 ArcTan[x, y]]^4;

fastDensityPlot[f[x, y], {x, -3, 3}, {y, -3, 3}, PlotPoints -> 10, 
 MaxRecursion -> 2, ColorFunction -> Hue, Subpoints -> 20, 
 PlotRange -> All, ImageSize -> 600, Exclusions -> None]

enter image description here

This image looks a bit better. At the same time fastDensityPlot is ~10 times faster then the regular DensityPlot, MaxMemoryUsed is only 64MB and ByteCount is 10MB.

One can see that fastDensityPlot uses the advantage of the non-equidistant mesh:

fastDensityPlot[f[x, y], {x, -3, 3}, {y, -3, 3}, PlotPoints -> 10, 
 MaxRecursion -> 2, ColorFunction -> Hue, Subpoints -> 20, 
 PlotRange -> All, ImageSize -> 600, Mesh -> All, Exclusions -> None]

enter image description here

Definitions of the above functions:

toTriangles = # /. Polygon[v_ /; Length[v] > 3, ___] :> (Polygon@Append[#, Mean[v]] & /@ 
       Partition[v, 2, 1, 1]) &;

texturize[f_, n_, colf_] := # /. Polygon[{v1_, v2_, v3_}, ___] :> {Texture@
       ImageData@Colorize[
         Image@f[v3[[1]] + (v1[[1]] - v3[[1]]) #1 + (v2[[1]] - v3[[1]]) #2, 
                v3[[2]] + (v1[[2]] - v3[[2]]) #1 + (v2[[2]] - v3[[2]]) #2] 
          &[#, Transpose[#]] &@ConstantArray[Range[-1./n, 1 + 1./n, 1./n], n + 3], 
         ColorFunction -> colf, ColorFunctionScaling -> False], 
      Polygon[{v1, v2, v3}, 
       VertexTextureCoordinates -> {{1 - 1.5/(n + 3), 
          1 - 1.5/(n + 3)}, {1.5/(n + 3), 1.5/(n + 3)}, {1.5/(n + 3), 
          1 - 1.5/(n + 3)}}]} &;

As in the linked answer, I add textures to every triangle with an appropriate rectangular grid. This method is fast because it uses packed arrays.

ybeltukov
  • 43,673
  • 5
  • 108
  • 212
  • As I understand, you subdivide each triangle into Subpoints^2 regions and calculate values of f in the nodes. Is it correct? – Alexey Popkov Jan 05 '14 at 14:24
  • Yes! More precisely, I calculate the texture for a parallelogram and use only a half of it. One can optimize it further, but it is no so simple. – ybeltukov Jan 05 '14 at 14:30
  • So PlotPoints->n, Subpoints->m, MaxRecursion->0 should be equivalent to PlotPoints->n*m, Subpoints->0, MaxRecursion->0? – Alexey Popkov Jan 05 '14 at 14:45
  • @AlexeyPopkov Yes, but with Subpoints->1 in the second case. – ybeltukov Jan 05 '14 at 15:03
  • With your screenshot I see that these plots are not identical. I also wonder why ByteCount for the first plot returns 883816 while for the second plot it gives 34814392? – Alexey Popkov Jan 05 '14 at 15:38
  • 1
    @AlexeyPopkov They are visually similar, but not equivalent. The second one contains a lot of small triangles with a lot of small textures. This is very slow and inefficient. fastDensityPlot is fast when you use a small number of big triangles with big textures (two big triangles in the limit). The opposite limit is DensityPlot which produce a lot of small colorized (not texturized) triangles. – ybeltukov Jan 05 '14 at 15:52
  • Now I got it, clever idea! But I should note that the restriction for fastDensityPlot accepting only a PureFunction which must contain only Listable functions makes it unusable for many practical applications where these requirements are unfeasible. This requirement also makes your code very difficult for debugging and understanding. – Alexey Popkov Jan 05 '14 at 16:55
  • @AlexeyPopkov There is no restrictions to pure functions. You can use a regular pattern definition f[x_,y_] := .... Without Listable I will lose advantages of packed arrays. In any case you can: 1) add Listable attribute to f, 2) use Interpolation which is Listable, 3) Compile with Listable attribute. – ybeltukov Jan 05 '14 at 18:01
  • When I evaluate SetAttributes[f,Listable]; f[x_,y_]:=(x^2+y^2) Exp[-x^2-y^2] Sin[10 Sqrt[x^2+y^2]+10 ArcTan[x,y]]^4; fastDensityPlot[f[x,y],{x,-3,3},{y,-3,3},PlotPoints->10,MaxRecursion->2,ColorFunction->Hue,Subpoints->20,PlotRange->All] I get many error messages and empty graphics. – Alexey Popkov Jan 05 '14 at 18:37
  • Hi, @ybeltukov. Your function seems doesn't work for mma 11.0.1.0. This is what I got https://pasteboard.co/avzFaWmrL.png – matheorem May 25 '17 at 11:36
  • Apparently this doesn't work in Mathematica 10.1 under Windows; for the first output shown I get https://i.stack.imgur.com/3ydqE.png -- any thoughts about that? – Mr.Wizard Dec 19 '19 at 13:06
  • @Mr.Wizard Nice to see your comment again! Somehow the substitution in toTriangles does not work. Could you check the full form of your result? Is there any Polygon[...] expressions? – ybeltukov Dec 19 '19 at 19:40
  • Thanks! I'm glad you're here to talk to. :-) Yes, in the InputForm of the graphic from my comment above there are many Polygon expressions, but it looks like your pattern doesn't match because of VertexColors? Here's one: Polygon[{{2.833333333333333, -2.8333333333333335}, {3., -2.666666666666667}, \ {2.666666666666666, -2.666666666666667}}, VertexColors -> {Hue[1.236068478360077*^-6], Hue[4.324625545000996*^-6], Hue[0.00002590501983329312]}] – Mr.Wizard Dec 19 '19 at 22:09
  • @Mr.Wizard Actually toTriangles does nothing because this polygon is already a triangle. The problem is somewhere further. Does the final result contain texturized triangles of the form {Texture[{...}],Polygon[{...},VertexTextureCoordinates->{...}]}? If so, it is a frontend problem. – ybeltukov Dec 20 '19 at 19:46
  • 2
    To my embarrassment the problem was localized to the region between the keyboard and the chair. The definitions of toTriangles and texturize were being loaded from (38688) rather than this answer. – Mr.Wizard Dec 22 '19 at 01:16