Let f be some numeric function with two arguments

f[x_?NumericQ, y_?NumericQ] := x y

For the purposes of this question f has a very simple form, but the function I am actually working with takes several seconds to evaluate. Therefore, I want to store every evaluation of f. This could be done with a memory function (memf[x_,y_]:=memf[x,y]=f[x,y]), but here I use Sow instead, i.e.

sowf[x_?NumericQ, y_?NumericQ] := (Sow[{x, y, f[x, y]}, "f"]; f[x, y])

Now create a ContourPlot of sowf with

{plot, {{fPts}, {monitorPts}}} = Reap[
   ContourPlot[
     sowf[x, y], {x, 0, 1}, {y, 0, 1},
     EvaluationMonitor :> Sow[{x, y, f[x, y]}, "monitor"],
     PlotTheme -> "Monochrome",
     PlotPoints -> 3,
     MaxRecursion -> 0
   ],
   {"f", "monitor"}
];

The plot is now stored in plot, while fPts contains all results of evaluations of sowf and monitorPts contains all points that where sent to EvaluationMonitor.

Remark: I thought those two sets of points should be the same, but in fact sowf was evaluated one more time than EvaluationMonitor suggests:

Length /@ {fPts, monitorPts}
(* {10, 9} *)

Complement[fPts, monitorPts]
(* {{0.0005, 0.0005, 2.5*10^-7}} *)

The computation above will be done with the expensive f on a computer cluster, and I want to make some adjustments to the plot style and other options later on, without having to re-evaluate f.

My question is, how can I use either set of points to reconstruct the ContourPlot, using a ListContourPlot? Simply providing ListContourPlot with one of those sets results in a different plot:

contours = Sort@Cases[plot, Tooltip[__, n_] :> n, \[Infinity]]
(* {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9} *)

Show[{
  plot,
  ListContourPlot[monitorPts, 
   ContourStyle -> Directive[Thick, Cyan, Dashed], 
   ColorFunction -> (Transparent &), Contours -> contours]
}]

enter image description here

Show[{
  plot,
  ListContourPlot[fPts, 
   ContourStyle -> Directive[Thick, Cyan, Dashed], 
   ColorFunction -> (Transparent &), Contours -> contours]
}]

enter image description here

Note, that ListContourPlot does not show all the contours I specified! The contours for 0.9, 0.8, and 0.7 are missing.

Tally@Cases[%, Tooltip[__, n_] :> n, \[Infinity]]
(* {{0.9, 1}, {0.8, 1}, {0.7, 1}, {0.6, 2}, {0.5, 2}, {0.4, 2}, {0.3, 2},
{0.2, 2}, {0.1, 2}} *)

What additional information does ContourPlot use?

When the number of points is large enough it works fine

{plot, {{fPts}, {monitorPts}}} = Reap[
   ContourPlot[
    sowf[x, y], {x, 0, 1}, {y, 0, 1},
    EvaluationMonitor :> Sow[{x, y, f[x, y]}, "monitor"],
    PlotTheme -> "Monochrome"
    ],
   {"f", "monitor"}
   ];
Show[{
  plot,
  ListContourPlot[fPts, 
   ContourStyle -> Directive[Thick, Cyan, Dashed], 
   ColorFunction -> (Transparent &)]
}]

enter image description here

but the actual f is, as I mentioned before, very expensive to evaluate, so I want to keep the number of plot points relatively low.

  • 3
    Which Mathematica version do you use? With versions 11.1.1 and 11.2.0 I get all the contours. But versions 8.0.4 and 10.4.1 do not generate contours for 0.9, 0.8, and 0.7. – Alexey Popkov Sep 19 '17 at 10:14
  • 1
    Interesting. I use 11.0.1. – JEM_Mosig Sep 24 '17 at 8:20

[Partial Answer - Please add your own answers as well!]

I cannot really explain why ListContourPlot and ContourPlot give different results, but there is a workaround using only ContourPlot.

We can create the plot as before

{plot, {fPts}} = Reap[
   ContourPlot[
    sowf[x, y], {x, 0, 1}, {y, 0, 1},
    PlotTheme -> "Monochrome",
    PlotPoints -> 3,
    MaxRecursion -> 0
   ]
];

and then define a memory-function

ClearAll[memf];
Do[memf[p[[1]], p[[2]]] = p[[3]], {p, fPts}];

to be used by ContourPlot with exactly the same settings for PlotPoints and MaxRecursion as before.

This requires, however, some amount of faith in Wolfram not changing anything in the ContourPlot routine from one MMA version to the next.

Probably, ContourPlot and ListContourPlot use different Methods for the same set of function points. See, for instance, the first answer to question 69448. Not having the patience to experiment with these many possibilities, I suggest building an Interpolation function from monitorPts, and plot it with ContourPlot.

int = Interpolation[intpts = {Most@#, Last@#} & /@ monitorPts, 
    InterpolationOrder -> 2];
Show[{plot, ContourPlot[int[x, y], {x, 0, 1}, {y, 0, 1}, PlotPoints -> 3, 
    MaxRecursion -> 0, ContourStyle -> Directive[Thick, Cyan, Dashed], 
    ColorFunction -> (Transparent &), Contours -> contours], ImageSize -> Large}]

enter image description here

InterpolationOrder -> 2 can be omitted for larger values of PlotPoints.

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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