Function Repository Resource:

MonitorEvaluate

Source Notebook

Display progress information during the evaluation of an expression

Contributed by: Giulio Alessandrini

ResourceFunction["MonitorEvaluate"][expr]

displays progress information during the evaluation of expr.

ResourceFunction["MonitorEvaluate"][expr, params]

displays progress according to the specification in params.

Details and Options

ResourceFunction["MonitorEvaluate"] is used to communicate the status of a long evaluations, like file processing, downloads, etc.
The params argument must be an Association or List of Rule expressions.
An association formats as a panel, a list of rules as a table:
When using an association, the most commonly used keys are:
"Text"main progress text
"Progress"current progress value between 0 and 1
"Detail"secondary progress text
Additionally, the following keys are also supported:
"ByteCountCurrent"current processed data
"ByteCountTotal"total data to process
"ElapsedTime"time since the beginning of the evaluation
"ItemAction"action performed on the items
"ItemCurrent"amount of processed items
"ItemName"name of the processed items
"ItemTotal"number of items to process
"Percentage"progress percentage
"RemainingTime"time to the end of the evaluation
When using a list of rule, any LHS can be used. However, some have special interpretation:
Left|Center|Rightdisplay only the RHS with the specified alignment
"Progress"display a center aligned progress bar
The following options are supported:
"Container"Nonevariable that will contain the progress info
DeinitializationNoneexpression to evaluate after expr
"Delay"2minimal number of seconds after which the indicator is printed
InitializationNoneexpression to evaluate before expr
"OuterUpdateInterval"Automaticrefresh rate of the indicator panel
"StopButton"Nonefunction triggered by a stop button
"TimeEstimate"Indeterminateinitial time estimate
"Title"Automatictitle used in the table
UpdateInterval0.4refresh rate of the indicator values

Examples

Basic Examples (3) 

Display a generic progress panel:

In[1]:=
ResourceFunction["MonitorEvaluate"][Pause[5]]

Display a more detailed progress information:

In[2]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 5}], <|"Text" -> "Evaluating table...", "Progress" -> (i - 1)/5|>]

Display a custom information grid:

In[3]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[.3], {i, 0, 1, 1/100}],
 {"ElapsedTime" -> Automatic,
  "RemainingTime" -> Automatic,
  "Percentage" -> i,
  "CurrentFactor" :> If[NumberQ[i], FactorInteger[i], ""],
  "Progress" -> i},
  "Delay" -> 0,
 "Title" -> "My computation"
 ]

Scope (17) 

Basic Specifications (4) 

Display the default panel:

In[4]:=
ResourceFunction["MonitorEvaluate"][Do[Pause[1], {i, 0, 5}]]

Display custom text:

In[5]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 5}], <|"Text" -> "My long computation..."|>]

Display detail information:

In[6]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1]; i, {i, 0, 5}],
 <|"Text" -> "My long computation...", "Detail" -> "Iterating over many possibilities"|>]

Display a progress bar:

In[7]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 5}], <|"Text" -> "My long computation...", "Progress" -> i/5, "Detail" -> "Iterating over many possibilities"|>]

Time (4) 

Display elapsed time:

In[8]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 5}], <|"ElapsedTime" -> Automatic|>]

Display remaining time:

In[9]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 5}], <|"RemainingTime" -> 5 - i|>]

Automatically estimate the remaining time using progress information:

In[10]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 5}], <|"Progress" -> i/5, "RemainingTime" -> Automatic|>]

In[11]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 5}], <|"Percentage" -> i/5, "RemainingTime" -> Automatic|>]

Percentage (1) 

Display progress percentage:

In[12]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 5}], <|"Percentage" -> i/5|>]

Byte Count (2) 

Display byte count information:

In[13]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 5}], <|"ByteCountCurrent" -> 10^4 i|>]

Display processed and total size:

In[14]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 5}], <|"ByteCountCurrent" -> 10^4 i, "ByteCountTotal" -> 10^4 5|>]

Items (4) 

Display processed items:

In[15]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 5}], <|"ItemCurrent" -> i|>]

Specify the total amount of items:

In[16]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 5}], <|"ItemCurrent" -> i, "ItemTotal" -> 5|>]

Specify the item name:

In[17]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 5}], <|"ItemCurrent" -> i, "ItemTotal" -> 5, "ItemName" -> "expressions"|>]

Specify the action performed on the items:

In[18]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 5}], <|"ItemCurrent" -> i, "ItemTotal" -> 5, "ItemName" -> "expressions", "ItemAction" -> "Parsing"|>]

Grid layout (2) 

Typeset the progress panel as a grid:

In[19]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 10}],
 {
  "Text" -> "Very complex computation", "Detail" -> "We really have a lot to say",
  "ElapsedTime" -> Automatic, "RemainingTime" -> Automatic, "Percentage" -> i/10,
  "ByteCountCurrent" -> 10^4 i, "ByteCountTotal" -> 10^5
  }
 ]

Mark some keys as optional:

In[20]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 0, 10}],
 {
  "Text" -> "Very complex computation", "Detail" -> "We really have a lot to say",
  Optional@"ElapsedTime" -> Automatic, "RemainingTime" -> Automatic, Optional@"Percentage" -> i/10,
  "ByteCountCurrent" -> 10^4 i, Optional@"ByteCountTotal" -> 10^5
  }
 ]

Options (12) 

Container (2) 

By default, the progress panel is printed in a temporary cell:

In[21]:=
ResourceFunction["MonitorEvaluate"][Pause[5]]

Specify a "container" variable that will be assigned the panel:

In[22]:=
Clear[x]
Dynamic[x]
Out[23]=
In[24]:=
ResourceFunction["MonitorEvaluate"][Pause[5], "Container" -> x]
In[25]:=
Clear[x]

Use integers or string to specify a container that will be automatically created and cleaned up:

In[26]:=
ResourceFunction["MonitorEvaluate"][
 Pause[1];
 ResourceFunction["MonitorEvaluate"][
  Pause[1];
  ,
  <|"Text" -> 2|>, "Container" -> 1, "Delay" -> 0];
 Pause[1]
 ,
 <|"Text" -> 1|>, "Container" -> 1, "Delay" -> 0]

Mix different containers:

In[27]:=
ResourceFunction["MonitorEvaluate"][
 Pause[1];
 ResourceFunction["MonitorEvaluate"][
  Pause[1];
  ,
  <|"Text" -> 2|>, "Container" -> 2, "Delay" -> 0];
 ResourceFunction["MonitorEvaluate"][
  Pause[1];
  ,
  <|"Text" -> 3|>, "Container" -> 1, "Delay" -> 0];
 Pause[1]
 ,
 <|"Text" -> 1|>, "Container" -> 1, "Delay" -> 0]

Delay (2) 

By default, the progress panel appears after two seconds:

In[28]:=
ResourceFunction["MonitorEvaluate"][Pause[5]]

Have it appearing immediately:

In[29]:=
ResourceFunction["MonitorEvaluate"][Pause[5], "Delay" -> 0]

OuterUpdateInterval (2) 

Updating the interpretation of some keys require a redraw of the whole panel:

In[30]:=
Block[{p = None},
 ResourceFunction["MonitorEvaluate"][
  Pause[3];
  Do[Pause[.2]; If[OddQ[i], p = i/20, p = None];, {i, 0, 20}],
  <|"Progress" :> p, "ElapsedTime" -> Automatic|>
  ]
 ]

Specify how often the redraw check should be performed:

In[31]:=
Block[{p = None},
 ResourceFunction["MonitorEvaluate"][
  Pause[3];
  Do[Pause[.2]; If[OddQ[i], p = i/20, p = None];, {i, 0, 20}],
  <|"Progress" :> p, "ElapsedTime" -> Automatic|>,
  "OuterUpdateInterval" -> 0
  ]
 ]

StopButton (2) 

Define a behaviour for when the computation is stopped:

In[32]:=
Block[{stop = False, list = {}},
 ResourceFunction["MonitorEvaluate"][
  Catch@Table[
    If[stop, Throw[list]];
    Pause[.5];
    AppendTo[list, i];
    i,
    {i, 20}]
  ,
  <|"Text" -> "Evaluating table...", "Progress" -> (i - 1)/20|>,
  "StopButton" :> {stop = True}
  ]
 ]

Define a stop behaviour using the grid layout:

In[33]:=
Block[{stop = False, list = {}},
 ResourceFunction["MonitorEvaluate"][
  Catch@Table[
    If[stop, Throw[list]];
    Pause[.3];
    AppendTo[list, i];
    i,
    {i, 50}]
  ,
  {"Text" -> "Evaluating table...", "Last processed" :> Last[list, None]},
  "StopButton" :> {stop = True}
  ]
 ]

TimeEstimate (2) 

The remaining time can be automatically estimated using a progress key:

In[34]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[.1], {i, 0, 100}], <|"RemainingTime" -> Automatic, "Progress" :> i/100|>, "Delay" -> 0]
In[35]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[.1], {i, 0, 100}], <|"RemainingTime" -> Automatic, "ItemCurrent" :> i, "ItemTotal" -> 100|>, "Delay" -> 0]

If progress information cannot be obtained at runtime, some values can be driven by an initial estimate:

In[36]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[2], {i, 0, 5}], <|"RemainingTime" -> Automatic, "Progress" -> Automatic|>, "Delay" -> 0, "TimeEstimate" -> 11]

UpdateInterval (2) 

The default update interval is picked automatically:

In[37]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[.1], {i, 0, 50}], <|"Text" :> RandomReal[], "Progress" -> i/50|>]

Manually specify an interval:

In[38]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[.1], {i, 0, 50}], <|"Text" :> RandomReal[], "Progress" -> i/50|>, UpdateInterval -> .1]

Applications (3) 

Build a monitored version of Map:

In[39]:=
monitoredMap[expr1_, expr2_, level_ : 1] :=
 Block[{i, max, bag, stop, res}, i = 0; bag = Internal`Bag[]; max = Quiet[
    Check[Length[Level[expr2, level, Hold]], Indeterminate]]; ResourceFunction["MonitorEvaluate"][Catch[
    MapIndexed[(
       res = expr1[#1]; i++; Internal`StuffBag[bag, #2 -> res]; If[stop, Throw[ReplacePart[expr2, Internal`BagPart[bag, All]]]]; res) &, expr2, level]],
   <|"Text" -> StringForm["Mapping ``...", expr1], "Progress" :> i/max, "RemainingTime" :> Automatic|>,
   "Delay" -> 0, "StopButton" :> (stop = True)]
  ]
In[40]:=
func = (Pause[.1]; f[#]) &;
In[41]:=
monitoredMap[func, Partition[Range[100], 2]]
Out[41]=

Use a different level spec:

In[42]:=
monitoredMap[func, Partition[Range[100], 2], 2]
Out[42]=

Create a monitored version of NDSolveValue that can return a partial solution:

In[43]:=
Block[
     {progress = None, stop = False, res},
     ResourceFunction["MonitorEvaluate"][
          NDSolveValue[
               {
                    D[u[t, x], t, t] == D[u[t, x], x, x],
                    u[0, x] == Exp[-10 x^2],
                    Derivative[1, 0][u][0, x] == 0,
                    u[t, -10] == u[t, 10], WhenEvent[If[NumericQ[t] && t > 10, progress = t/100]; stop, "StopIntegration"]
                 },
                   u,
                   {t, 0, 100},
                   {x, -10, 10},
                   Method -> "StiffnessSwitching"
           ],
          <|
               "Text" -> "Making awsomeness...",
               "Progress" :> progress,
               "ElapsedTime" -> Automatic,
               "RemainingTime" -> Automatic
           |>,
          "Delay" -> 0
          ,
          "StopButton" :> (stop = True)
      ]
 ]
Out[43]=

Create a monitored version of NDSolveValue that show the current MemoryInUse[].

Write a function to plot the memory:

In[44]:=
memoryPlot[list_] := Framed[Quiet@
   ListLinePlot[list, ImageSize -> 330, AspectRatio -> 2/5, PlotLabel -> "Memory in use", AxesLabel -> Automatic], Background -> White, FrameStyle -> GrayLevel[0.7], RoundingRadius -> 3, ImageMargins -> 5]

During the computation, update the progress at each step but save the memory at a lower interval:

In[45]:=
DynamicModule[
     {progress = 0, list = {Quantity[MemoryInUse[]/10.^6, "Megabytes"]}},
     ResourceFunction["MonitorEvaluate"][
          NDSolveValue[
               {
                    D[u[t, x], t, t] == D[u[t, x], x, x],
                    u[0, x] == Exp[-10 x^2],
                    Derivative[1, 0][u][0, x] == 0,
                    u[t, -10] == u[t, 10]
                 },
                   u,
                   {t, 0, 100},
                   {x, -10, 10},
                   Method -> "StiffnessSwitching",
   StepMonitor :> {If[NumericQ[t],
       progress = SetPrecision[t/100., MachinePrecision];
      If[Mod[Round@t, 5] == 0,
       AppendTo[list, Quantity[MemoryInUse[]/10.^6, "Megabytes"]]
       ]
      ]}
           ],
          {
   "ElapsedTime" -> Automatic,
   "RemainingTime" -> Automatic,
   "Progress" :> progress,
   Center :> memoryPlot[list]
   },
     "Title" -> "NDSolveValue",
  "Delay" -> 0
      ]
 ]

Make the memory plot optional and use a Dynamic wrapper to force it to update only each 3 seconds:

In[46]:=
DynamicModule[
     {progress = 0, list = {Quantity[MemoryInUse[]/10.^6, "Megabytes"]}},
     ResourceFunction["MonitorEvaluate"][
          NDSolveValue[
               {
                    D[u[t, x], t, t] == D[u[t, x], x, x],
                    u[0, x] == Exp[-10 x^2],
                    Derivative[1, 0][u][0, x] == 0,
                    u[t, -10] == u[t, 10]
                 },
                   u, {t, 0, 100},  {x, -10, 10},
                   Method -> "StiffnessSwitching",
   StepMonitor :> {If[NumericQ[t],
       progress = SetPrecision[t/100., MachinePrecision];
      If[Mod[Round@t, 5] == 0,
       AppendTo[list, Quantity[MemoryInUse[]/10.^6, "Megabytes"]]
       ]]}
           ],
          {
   "ElapsedTime" -> Automatic, "RemainingTime" -> Automatic,
   "Progress" :> progress, Optional[Center] :> Dynamic[memoryPlot[list], UpdateInterval -> 3, TrackedSymbols :> {}]
   },
     "Title" -> "NDSolveValue", "Delay" -> 0
      ]
 ]

Properties and Relations (2) 

MonitorEvaluate respects the $ProgressReporting setting:

In[47]:=
ResourceFunction["MonitorEvaluate"][
 Do[Pause[1], {i, 5}], <|"Text" -> "Evaluating table...", "Progress" -> (i - 1)/5|>, "Delay" -> 0]
In[48]:=
Block[{$ProgressReporting = False},
 ResourceFunction["MonitorEvaluate"][
  Do[Pause[1], {i, 5}], <|"Text" -> "Evaluating table...", "Progress" -> (i - 1)/5|>, "Delay" -> 0]
 ]

MonitorEvaluate works similarly to Monitor:

In[49]:=
Monitor[Table[Pause[.1]; Length[FactorInteger[2^n - 1]], {n, 50, 300, 50}], n]
Out[49]=
In[50]:=
ResourceFunction["MonitorEvaluate"][
 Table[Pause[.1]; Length[FactorInteger[2^n - 1]], {n, 50, 300, 50}], <|
  "Text" :> n|>, UpdateInterval -> 0, "Delay" -> 0]
Out[50]=

Possible Issues (2) 

Variables are not automatically scoped:

In[51]:=
n = "value";
ResourceFunction["MonitorEvaluate"][
 Table[Pause[.2]; n, {n, 10}], <|"Text" -> n|>, "Delay" -> 0]
Out[41]=

Use delayed assignments or scoping to avoid collisions:

In[52]:=
ResourceFunction["MonitorEvaluate"][
 Table[Pause[.2]; n, {n, 10}], <|"Text" :> n|>, "Delay" -> 0]
Out[52]=
In[53]:=
Block[{n},
 ResourceFunction["MonitorEvaluate"][
  Table[Pause[.2]; n, {n, 10}], <|"Text" -> n|>, "Delay" -> 0]
 ]
Out[53]=

Version History

  • 1.0.0 – 10 January 2023

Related Resources

License Information