Fittype that calls a subfunction and integration with appdesigner
2 views (last 30 days)
Show older comments
I have a subfunction that traditionally has sat in its own .m file. I'd like to integrate into an app code so everything is consolidated when I go to share it. However, the problem is the subfunction has to be called by "fittype". I think this is where I'm running into issues. The App can see the subfunction, but fittype can't see the function if its in the app.
I'm not sure if there's a way to set up the function in AppDesigner so that the "fittype" can see the subfunction. I tried making it both a function of the App, as well as as a nested type function where "fittype" is called. Neither worked.
The subfunction is very simple. It's a setup to let me do a Legendre polynomial fit:
function m0coeff = mod_legendre(n,x)
%Legendre Polynomial return: note that MatLab legendre function returns all
%coefficients from m=0...n, whereas the Legendre Polynomial is m=0 only.
LegCoef = legendre(n,x);
m0coeff = LegCoef(1,:)';
end
This then is called within appdesigner. I have to make a custom equation fit, and it needs to use the above function when it does the fit.
opts = fitoptions( 'Method', 'NonlinearLeastSquares' );
opts.Robust ='On';
opts.Display ='Off';
legnum = app.NumLegModes.Value;
for ii = 0:legnum
if ii==0
eqn = ['p' num2str(ii) '*mod_legendre(' num2str(ii) ',x)'];
opts.StartPoint =1;
else
eqn = [eqn '+p' num2str(ii) '*mod_legendre(' num2str(ii) ',x)'];
opts.StartPoint =[opts.StartPoint 0];
end
end
ft = fittype(eqn);
[legfit, gof, output] = fit(x,r,ft,opts);
The actual error is:
Error using fittype/testCustomModelEvaluation (line 12)
Expression
p0*mod_legendre(0,x)+p1*mod_legendre(1,x)+p2*mod_legendre(2,x)+p3*mod_legendre(3,x)+p4*mod_legendre(4,x) is
not a valid MATLAB expression, has non-scalar coefficients, or cannot be evaluated:
Error in fittype expression ==>
p0.*mod_legendre(0,x)+p1.*mod_legendre(1,x)+p2.*mod_legendre(2,x)+p3.*mod_legendre(3,x)+p4.*mod_legendre(4,x)
??? Undefined function 'mod_legendre' for input arguments of type 'double'.
Error in fittype>iCreateFittype (line 362)
testCustomModelEvaluation( obj );
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in fittype (line 319)
obj = iCreateFittype( obj, varargin{:} );
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in dblshell_Shape/leg_fit (line 483)
ft = fittype(eqn);
^^^^^^^^^^^^
Error in dblshell_Shape/LegendreLeastSquareButtonPushed (line 2162)
handles = leg_fit(app, handles);
^^^^^^^^^^^^^^^^^^^^^
Error in matlab.apps.AppBase>@(source,event)executeCallback(ams,app,callback,requiresEventData,event) (line 60)
newCallback = @(source, event)executeCallback(ams, ...
^^^^^^^^^^^^^^^^^^^^^^^^
Caused by:
Error using fittype/evaluate (line 57)
Error in fittype expression ==>
p0.*mod_legendre(0,x)+p1.*mod_legendre(1,x)+p2.*mod_legendre(2,x)+p3.*mod_legendre(3,x)+p4.*mod_legendre(4,x)
??? Undefined function 'mod_legendre' for input arguments of type 'double'.
2 Comments
dpb
on 14 Mar 2025
I've never had any issue with an m-file not being found if it is in the working path of the app...I don't see why it wouldn't also be visible here, but without the actual app code structure it's not possible to see what might be going on.
Matt J
on 14 Mar 2025
Your model is linear (in p). Using a nonlinear least squares fitter is super inefficient.
Accepted Answer
Steven Lord
on 14 Mar 2025
Try specifying the input to fittype not as a text expression but as an anonymous function that calls the local function inside the anonymous function.
4 Comments
Walter Roberson
on 14 Mar 2025
This is exactly the problem: quoted text is evaluated in the base workspace, outside the context that defines local functions. Use anonymous functions instead of quoted text.
More Answers (2)
dpb
on 14 Mar 2025
Edited: dpb
on 16 Mar 2025
As I expected, if the subfunction is an m-file in the working space, it works just fine...I built a toy app that mimics your posted code attached -- it consists of a spinner set to 4 and a button that calls your function with fake data...
% Callbacks that handle component events
methods (Access = private)
% Button pushed function: Button
function ButtonPushed(app, event)
opts = fitoptions( 'Method', 'NonlinearLeastSquares' );
opts.Robust ='On';
opts.Display ='Off';
legnum = app.NumLegModesSpinner.Value;
for ii = 0:legnum
if ii==0
eqn = ['p' num2str(ii) '*mod_legendre(' num2str(ii) ',x)'];
opts.StartPoint =1;
else
eqn = [eqn '+p' num2str(ii) '*mod_legendre(' num2str(ii) ',x)'];
opts.StartPoint =[opts.StartPoint 0];
end
end
ft = fittype(eqn);
x=linspace(-1,1,20); x=x(:);
r=rand(size(x));
[legfit, gof, output] = fit(x,r,ft,opts);
end
end
The fit function ran to completion although the function doesn't do anything with the outputs and they're not made part of the app properties struct so they're lost at the moment, but the code does run.
I put the mod_legendre function m-file in the same working folder; when you package the app, the dependency walker will find it is needed and include it as additional file needed.
This is a case as @Steven Lord notes, the way fittype is constructed the local function isn't in the base workspace scope in which MATLAB evaluates the passed string. The solution is to continue to use the m-file as demonstrated with the sample app or recast to use an anonymous function created in scope that refers to the local function.
Of course, this addresses only the structure of the app itself; @Matt J's observation that the model is linear in the coefficients is significant.
Matt J
on 14 Mar 2025
Edited: Matt J
on 14 Mar 2025
Your model is linear (in p). Using a nonlinear least squares fitter is super inefficient.
So, instead, you should do,
A = cell2mat(arrayfun(@(n)mod_legendre(n,x),0:legnum,'uni',0));
p=num2cell(A\r);
ft = cfit(eqn,p{:});
gof=gofCalc(ft,x)
function gof=gofCalc(ft,x,r)
r_fit = feval(ft, x);
% Compute goodness-of-fit statistics
SSE = sum((r - r_fit).^2); % Sum of squared errors
SST = sum((r - mean(r_fit)).^2); % Total sum of squares
R_square = 1 - (SSE / SST); % R-squared
RMSE = sqrt(SSE / length(r)); % Root Mean Square Error
% Create the gof structure
gof = struct('sse', SSE, 'rsquare', R_square, 'rmse', RMSE);
end
function m0coeff = mod_legendre(n,x)
%Legendre Polynomial return: note that MatLab legendre function returns all
%coefficients from m=0...n, whereas the Legendre Polynomial is m=0 only.
LegCoef = legendre(n,x);
m0coeff = LegCoef(1,:)';
end
0 Comments
See Also
Categories
Find more on Linear and Nonlinear Regression in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!