26
$\begingroup$

Normally, Plot acts like a black box and only gives you the requested plot once it has finished computing it.

However it can be useful, other than interesting, to see the unfinished plot in the act of being built. For example to get an immediate, rough idea of how a function behaves, without having to wait for the whole detailed graph to be computed.

How can I make Plot (or some other function) act like this?


I can simulate a similar behaviour using some loop which increases step by step the MaxRecursion option and shows each result, but this would require a lot more computational time because at each new value of MaxRecursion Plot would recompute all the previous recursions.

I can also make a "progressive plot" generating the list of point with something like

out={};Do[AppendTo[out,{x,x^2}],{x,0,10,0.1}];
Dynamic@ListPlot[out]

but this is no good either because I can't efficiently use recursion steps like Plot does and it gets slower and slower at increasing x values, probably because of the AppendTo creating a larger and larger list.

I looked around at options like EvaluationMonitor and StepMonitor but I didn't manage to understand how to use them for this purpose.

$\endgroup$
  • 2
    $\begingroup$ Take a look at this past question: How to obtain adaptive sampling as in Plot function?. I think the methods outlined there are more or less exactly what you are after. $\endgroup$ – MarcoB May 25 '15 at 11:28
  • 1
    $\begingroup$ Related: (216), (19121), (29346) $\endgroup$ – Mr.Wizard May 25 '15 at 12:26
  • 1
    $\begingroup$ I think you misunderstand my "related" links. I am not saying that a solution exists there or I would say "proposed duplicate" or something like that. Rather I am adding links to make related questions easier to find once one is found. Someone finding that question for example may be more interested in your question and since links go both ways this question will appear in the Linked sidebar of that one. $\endgroup$ – Mr.Wizard May 25 '15 at 12:58
  • 2
    $\begingroup$ I saw your update. Look at linked lists and undocumented "Bag" functionality as in (63363). If you can self-answer you should. :-) $\endgroup$ – Mr.Wizard May 25 '15 at 14:35
  • 2
    $\begingroup$ @Mr.Wizard I used Bag and it works like a charm! Thank you very much, I'll post soon the code I ended up using. The only catch is that for some reason a Bag object doesn't seem to trigger the evaluation of the expression in the Dynamic. As a workaround I added a variable which is changed as well and which triggers the expression in Dynamic, but I'd love to know if there is a cleaner way $\endgroup$ – glS May 25 '15 at 15:36
11
$\begingroup$

Here is a only slightly different approach. As I think it probably isn't essential in that case to actually see every single point appearing on its own it might be much more efficient and user friendly to just update at a given period without the actual code having to wait for the update to finish. I have also added some option handling:

SetAttributes[dynamicPlot, HoldAll]

Options[dynamicPlot] = Join[Options[Plot], Options[Dynamic]];

dynamicPlot[f_, {x_Symbol, x0_Real, x1_Real}, 
  opts : OptionsPattern[]] := Module[{
   bag = Internal`Bag[]
   },
  PrintTemporary[
   Dynamic[ListPlot[Sort@Internal`BagPart[bag, All],
     Evaluate[Sequence @@ FilterRules[{opts}, Options[ListPlot]]]
     ], Evaluate[Sequence @@ FilterRules[{opts}, Options[Dynamic]]], 
    UpdateInterval -> 0.5
    ]
   ];
  Plot[f[x], {x, x0, x1},
   EvaluationMonitor :> (Internal`StuffBag[bag, {x, f[x]}]),
   Evaluate[Sequence @@ FilterRules[{opts}, Options[Plot]]]
   ]
]    

you can then call it like e.g.:

dynamicPlot[NIntegrate[Sin[t] Exp[-t] t^8, {t, 0, #}] &, {x, 0., 20.},
 MaxRecursion -> 4, PlotRange -> All, UpdateInterval -> 0.1
]  

it seems to be stable on my machine, but I don't exactly understand how it is different from your code in that respect, I use trigger variables as in your code often and that alone seems to not be a problem.

It is probably also worth noting that the bag local variable in that case is colored red by the frontend as it seems to escape the Module. It is not entirely clear to me when an expression printed with PrintTemporary will be deleted and what that means for the local bag. It seems that the last instance of that variable survives but will be cleaned by the garbage collection at the next possibility so there is never more than one of them floating around...

$\endgroup$
  • $\begingroup$ Thanks, I didn't know about PrintTemporary which is way I finally ended up using Monitor. I too tried to put the Bag in a module but it gave me problems when using it inside Monitor. I also like the Options handling, which was something I failed to achieve in a clean way. $\endgroup$ – glS Jun 6 '15 at 15:43
  • $\begingroup$ Regarding the stability, I think it mostly depends on the function you apply dynamicPlot to. I tried it with the integral function defined in my original approach and it crashed the kernel in a couple of trials with longer (I used {x,0,50}) plotting ranges (it's pretty random though, it then worked fine for maybe a dozen of runs). My guess is that it depends on the way Bag looks for the sufficient amount of allocated memory or something like that. $\endgroup$ – glS Jun 6 '15 at 15:45
  • $\begingroup$ have you checked that the kernel crashes don't happen without the dynamic plots? NIntegrate is also a complicated function which might get into trouble. $\endgroup$ – Albert Retey Jun 6 '15 at 21:24
21
$\begingroup$

Thanks to the links and tips provided by Mr.Wizard I found the answer I was looking for, with a combination of EvaluationMonitor and the undocumented Bag functionality.

Two problems with the code I found:

  1. For some reason updating the Bag object doesn't trigger the evaluation of a Dynamic expression containing it. I had to add another variable which changing value triggers the update of the plot.
  2. The code is not stable. For some reason, more often than not it crashes the Kernel. I have no idea whatsoever why it does this.

Here is an example code:

Dynamic[a;
 ListPlot[Sort@Internal`BagPart[bag, All], Joined -> True, 
  ImageSize -> Large, PlotRange -> {{0, 100}, {0, 0.2}}]
 ]

bag = Internal`Bag[]; a = False;
integral[x_] := Abs@NIntegrate[Exp[I x (t + t^2)], {t, 0, 1}];
Plot[
 integral[x],
 {x, 0, 100},
 DisplayFunction -> (Null &),
 EvaluationMonitor :> (Internal`StuffBag[bag, {x, integral[x]}]; 
   a = ! a),
 MaxRecursion -> 4
 ]

I would have liked to have it all in a nicely callable function, but I don't know (yet) how to make the Dynamic@ListPlot show before the rest of the code is executed.

Here is a short sample of how it works:

enter image description here

EDIT:

Here is a cleaner solution, inspired by the other answer (this is the function used to generate the above gif):

SetAttributes[dynamicPlot, HoldAll]
Options[dynamicPlot] = Join[Options[Plot], Options[Dynamic]];
dynamicPlot[f_, {x_Symbol, x0_, x1_}, opts : OptionsPattern[]] :=
  Module[{bag = Internal`Bag[]},
    PrintTemporary @ Dynamic[
      ListPlot[
        Sort @ Internal`BagPart[bag, All],
        Evaluate[Sequence @@ FilterRules[{opts}, Options[ListPlot]]]
      ],
      Evaluate[Sequence @@ FilterRules[{opts}, Options[Dynamic]]],
      UpdateInterval -> 0.01
    ];
    Plot[f, {x, x0, x1},
      EvaluationMonitor :> (Internal`StuffBag[bag, {x, f}]),
      Evaluate[Sequence @@ FilterRules[{opts}, Options[Plot]]]
    ]
  ];
$\endgroup$
  • 1
    $\begingroup$ This is most awesome. I too would like to know, why it crashes the kernel, but your example is mesmerizing once it works. $\endgroup$ – LLlAMnYP May 25 '15 at 21:00
2
$\begingroup$

FWIW You can actually do this very simply with AppendTo:

a = {};
Monitor[Plot[y = Sqrt[x] Cos[1/x^(3/2)], {x, 0, 2},
  EvaluationMonitor :> (Pause[.01] ; AppendTo[a, {x, y}])], 
  ListPlot[a]]

enter image description here

I suppose if the function eval is so slow that you want to do this in the first place, you might not care about the performance hit associated with AppendTo.

I don't know if that might avoid the stability issue.

$\endgroup$
  • $\begingroup$ well this was basically my starting point, but there are situations in which the slow down given by AppendTo is significant enough, but you are still interested in looking at the ongoing evaluation. Basically if you want to look at the progress of a long process $\endgroup$ – glS Mar 28 '17 at 19:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.