codecrap
codecrap

Reputation: 9

MATLAB plotting in a parfor-loop

I need to plot figures with subplots inside a parfor-loop, similar to this question (which deals more with the quality of the plots).

My code looks something like this:

parfor idx=1:numel(A)
    N = A(idx);
    fig = figure();
    ax = subplot(3,1,1);
    plot(ax, ...);
    ...
    saveas(fig,"..."),'fig');
    saveas(fig,"...",'png');
end

This gives a weird error:

Data must be numeric, datetime, duration or an array convertible to double.

I am sure that the problem does not lie in non-numeric data as the same code without parallelization works.
At this point I expected an error because threads will concurrently create and access figures and axes objects, and I do not think it is ensured that the handles always correspond to the right object (threads are "cross-plotting" so to say).

If I pre-initialize the objects and then acces them like this,

ax = cell(1,numel(A)); % or ax = zeros(1,numel(A)); 
ax(idx) = subplot(3,1,1);

I get even weirder errors somewhere in the fit-calls I use:

Error using curvefit.ensureLogical>iConvertSubscriptIndexToLogical (line 26)
    Excluded indices must be nonnegative integers that reference the fit's input data points
Error in curvefit.ensureLogical (line 18)
    exclude = iConvertSubscriptIndexToLogical(exclude, nPoints);
Error in cfit/plot (line 46)
    outliers = curvefit.ensureLogical( outliers, numel( ydata ) );

I have the feeling it has to work with some sort of variable slicing described in the documentation, I just can't quite figure out how.

Upvotes: 0

Views: 1467

Answers (1)

codecrap
codecrap

Reputation: 9

I was able to narrow the issues down to a fitroutine I was using.

TLDR: Do not use fitobjects (cfit or sfit) for plots in a parfor-loop!

Solutions:

  1. Use wrappers like nlinfit() or lsqcurvefit() instead of fit(). They give you the fit parameters directly so you can call your fitfunction with them when plotting.
  2. If you have to use fit() (for some reason it is the only one which was able to fit my data more or less consistently), extract the fit parameters and then call your fitfunction using cell expansion.

    fitfunc = @(a,b,c,d,e,x) ( ... );
    [fitobject,gof,fitinfo] = fit(x,y,fitfunc,fitoptions(..));
    vFitparam = coeffvalues(fitobject);
    vFitparam_cell = num2cell(vFitparam);
    plot(ax,x,fitfunc(vFitparam_cell{:},x), ... );
    

As far as I know fit() requires the function handle to have subsequent parameters (not a vector), so by using a cell you can avoid bloated code like this:

     plot(ax,x,fitfunc(vFitparam(1),vFitparam(2),vFitparam(3),vFitparam(4),vFitparam(5),x), ... );

Upvotes: 0

Related Questions