1

I realize calling functions dynamically using feval is inefficient but it's quite convenient in my application and works well, except in the case of one function. I just can't see what is causing it. The function works fine when called in the usual way but raises the error when called with feval (last line in below):

x = 200;
[y_mean, y_sigma, y_int] = lin_model_predict(model, x, vars, params);  % works fine

assert(round(y_mean, 4) == 139.7200);
assert(isequaln(y_sigma, nan));
assert(isequal(round(y_int, 4), [137.5938  141.8462]));

% Test again using feval
f_name = "lin_model_predict";  % this will later be set programatically
[y_mean, y_sigma, y_int] = feval(f_name, model, x, vars, params);  % raises error

I include the function code below for reference:

function [y_mean, y_sigma, y_int] = lin_model_predict(model, x, vars, ...
    params)
% [y_mean, y_sigma, x_int] = lin_model_predict(model, x, vars, params)
% Make predictions with a linear model of the form:
%
%   power = a + b * load 
%

    % Make predictions using model
    [y_mean, y_int] = predict(model, x, 'Alpha', vars.significance);

    % TODO: Is there a need for sigma estimates?
    y_sigma = nan(size(x));

end

Full error message:

Error using classreg.regr/CompactPredictor/feval
Too many output arguments.

Error in test_lin_models (line 46)
[y_mean, y_sigma, y_int] = feval(f_name, model, x, vars, params);

Matlab version: 2021b

Update

By replacing the last line with:

a = feval(f_name, model, x, vars, params);

I figured out that it is expecting one output argument.

By debugging this call I figured out that it is calling a method of a class in CompactPredictor.m located here:

/Applications/MATLAB_R2021b.app/toolbox/stats/classreg/+classreg/+regr/CompactPredictor.m

From the docstring of this function:

        function yPred = feval(model,varargin)
%FEVAL Evaluate model as a function
%    YPRED = FEVAL(M,X1,X2,...Xp) computes an array YPRED of predicted
%    values from the regression model M using predictor values X1, X2, ...,
%    Xp, where P is the number of predictors in M.  Each X argument must be
%    the same size, and must have the same type as the corresponding
%    predictor variable. The size of the YPRED output is the same as the
%    common size of the X inputs.

And in this case the model argument is now the string "lin_model_predict" which raises a new error Unrecognized method, property, or field 'NumPredictors' for class 'string'.

So it looks to me that the standard use of feval has been hijacked by this class method.

Not sure how to avoid this. Any ideas?

I decided to post the question on the Mathworks forum because it may require expert input:

Feel free to add answers there or here.

9
  • 1
    Yes, that’s probably not the version of feval you want. You can try typing which feval in your command window to further verify. Somehow this version got added to your path. Look into rmpath to remove it - or maybe just relaunch MATLAB. You can also use builtin to call a particular version of a function but you shouldn’t have to do that here. Commented Feb 24, 2023 at 20:35
  • And, by the way, use of feval isn’t terrible (it’s not the same thing as eval), but you may find it slightly more efficient to specify the function name with a function handle rather than a character vector. Commented Feb 24, 2023 at 20:39
  • Thanks @horchler. which feval returns the correct version: built-in (/Applications/MATLAB_R2021b.app/toolbox/matlab/lang/feval). But which feval(f_name, model, x, vars, params) returns /Applications/MATLAB_R2021b.app/toolbox/stats/classreg/+classreg/+regr/CompactPredictor.m % LinearModel method. I don't understand this since the first argument I am passing is a string, not the LinearModel. Commented Feb 24, 2023 at 20:42
  • Indeed, if you can use function handles, do that instead. I didn't think of that when I wrote the answer below. Commented Feb 24, 2023 at 20:47
  • 1
    If a function feval exists that is explicitly overloaded for the class of any of the input arguments, then that overload is called. MATLAB doesn't look at the number of arguments to decide which function to call, only at the types of the input arguments you give when you call it. If there are multiple such functions (an overload for parameter #1, one for #2, etc) then it picks the one for the leftmost of these arguments. Commented Feb 24, 2023 at 21:04

1 Answer 1

2

In your line

[y_mean, y_sigma, y_int] = feval(f_name, model, x, vars, params);

one of the arguments is of class CompactPredictor (maybe model?), which triggers the overloaded version of feval to be called. This is how MATLAB does overload resolution.

There are several ways around this. One is to not use objects of this class, maybe by writing lin_model_predict to take a single cell array as input, so that you'd call it with lin_model_predict({model, x, vars, params}). Another is to use builtin:

[y_mean, y_sigma, y_int] = builtin('feval', f_name, model, x, vars, params);

A third solution is to not use feval. Instead, you could write a function that calls the right function for you:

function [y_mean, y_sigma, y_int] = my_feval(function_name, varargin)
switch function_name
  case 'lin_model_predict':
    [y_mean, y_sigma, y_int] = lin_model_predict(varargin{:});
  case 'other_function':
    [y_mean, y_sigma, y_int] = other_function(varargin{:});
  otherwise:
    error(['Unknown function ', function_name])
end

As user horchler wrote in a comment, function handles are a much cleaner way to reference a function than through strings. With a function handle you'd do:

f_handle = @lin_model_predict; % instead of f_name = "lin_model_predict";
[y_mean, y_sigma, y_int] = f_handle(model, x, vars, params);

But there are situations where only a string makes sense: Maybe the function being referenced is a private function and you don't want the caller to have direct access to them. Or maybe the string comes from a pull-down menu in a GUI or a configuration file. In these cases you can use a table lookup to translate the string into a function handle, see containers.Map or, since R2022b, dictionary.

Sign up to request clarification or add additional context in comments.

3 Comments

These are great thanks. The most convenient for me I think is that builtin call. Didn't know about that.
Function handles are not an option for me. Even though I wrote the statement f_name = "lin_model_predict" in the code in the question, in fact this name string comes from a Yaml config text file and therefore may have any of an arbitrarily large set of values. As far as I know feval is the only way to call a function with only its name.
@Bill If you don't have a complete list of functions that can be chosen, then feval is your only option. Of course you could use eval as well, but that is really bad and should be avoided if possible.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.