User defined function that evaluates anonymous function with variable number of inputs

20 views (last 30 days)
I'm writing a user defined function that takes in a model of a system, represented as an anonymous function (this can be any multivariable model), and a value for each of the independent variables. The function then, for each independent variable, generates a vector of values distributed normally around the user input value of the independent variable. The function finally evaluates the model (anonymous function) with the vectors for each independent variable, thus generating a distribution of values for the model output. The function inputs are: 1. an anonymous function that represents a model of a system 2. size of distributions 3. value for 1st independent variable 4. distribution type of first variable (only accepts Gaussian at this point) and 5. standard deviation of first variable. The last three inputs can be repeated as many times as desired to enable a variable number of independent variables for the model.
The code for the function file is below.
function DV = ProbInpEx(varargin)
i = 2;
j = 1;
% This is error checking. Ignore.
% if nargin < 5
% disp('Input first independent variable information')
% elseif rem(length(varargin)-2,3) ~=0
% disp('Some independent variable information missing')
% end
% if rem(varargin{2},1) ~=0
% disp('Please input an integer for distribution size.')
% end
% End error checking
while i < length(varargin)
if varargin{i+2} == 'Gaussian' % Could use switch case for other distribution types
tempvariable = varargin{i+3}.*randn(varargin{2},1)+varargin{i+1};
eval(['x' num2str(j) '=tempvariable;']);
else
disp('Only accepts Gaussian distributions for indepentent variables')
end
j = j+1;
i = i+3;
end
DV = varargin{1} (x1,x2,x3,x4,x5) % This will provide the correct output I am looking forward. However, I cannot specificy x1,x2,...x5
% since the number of indepent variables
% varies depending on the model input.
% DV varargin{1} How can I evaluate this function when the independent variables are in the workspace?
end
An example script utilizing the function is below.
%% Example script using function.
DV = ProbInpEx(@(x1,x2,x3,x4,x5) 2.*x1-1.*x2-3.*x3+9.*x4+6.*x5,100,3,'Gaussian',.6,2,'Gaussian',.4,5,'Gaussian',1,90,'Gaussian',10,7,'Gaussian',.2) % Input model as anonymous function
% @(x1,x2,x3,x4,x5) 2.*x1-1.*x2-3.*x3+9.*x4+6.*x5 This is the model I want
% to evaluate in this specific example. I would like for the function to be
% able to accept any model with any number of independent variables.
The function works correctly until the last line (unless I specify, in the function file, the inputs into the anonymous function). The approach I've listed in the function file isn't suitable since it requires the function file to be changed each time the number of independent variables in the model to be evaluatd changes. How can I change this function to automatically evaluate the model (anonymous function) with a variable number of independent variables, all of which are present in the function's workspace?
Thank you!

Accepted Answer

Stephen23
Stephen23 on 11 May 2021
Edited: Stephen23 on 11 May 2021
Your mistake was deciding to use eval, which is one way that beginners paint themselves into a corner with slow, inefficient, complex code that is difficult to understand, hard to get working correctly, and makes debugging harder:
The MATLAB approach is to use a comma-separated list generated from a cell array:
For example:
fnh = @(x1,x2,x3,x4,x5) 2.*x1-1.*x2-3.*x3+9.*x4+6.*x5;
DV = ProbInpEx(fnh, 100, 3,'Gaussian',0.6, 2,'Gaussian',0.4, 5,'Gaussian',1, 90,'Gaussian',10, 7,'Gaussian',0.2)
DV = 100×1
939.1825 652.3294 673.4546 909.5006 831.4858 841.3921 948.2613 888.2769 798.5612 887.3944
Where
function out = ProbInpEx(fun,num,varargin)
ofs = [varargin{1:3:end}];
typ = varargin(2:3:end);
scl = [varargin{3:3:end}];
nml = numel(ofs);
csl = cell(1,nml); % preallocate cell array
for k = 1:nml
switch lower(typ{k})
case 'gaussian'
csl{k} = scl(k).*randn(num,1)+ofs(k);
case 'somethingelse'
otherwise
error('oh no!')
end
end
out = fun(csl{:}); % comma-separated list
end
Using equals to compare character vectors is also buggy, which you would have found out later when you tried other names with different numbers of characters in them. To compare strings use STRCMP or STRCMPI.
I recommend that using one non-scalar structure would be a better way to supply those input parameters, rather than as lots and lots of positional inputs. More than about six or seven positional arguments is fragile to work with.

More Answers (1)

Matt J
Matt J on 11 May 2021
Edited: Matt J on 11 May 2021
The code would be a lot more efficient and tidy if you had the anonymous function accept an MxN array X.
fun=@(X) X*[2,-1,-3,+9,+6].';
Mean=[3,2,5,90,7];
Std=[0.6,0.4,0.1,10,0.2];
ProbInpEx( fun,Mean,Std );
function DV = ProbInpEx(fun, M, Mean,Std)
N=numel(Mean);
assert(N==numel(Std),'Mean and Std must be equal length vectors')
DV=fun( randn([M,N]).*Std(:).' + Mean(:).' );
end
  2 Comments
Stephen23
Stephen23 on 11 May 2021
Yes, I also considered this, but it seems that the intent is to allow for different distributions.
Matt J
Matt J on 11 May 2021
Edited: Matt J on 11 May 2021
If the distribution types can mix a great deal, with different numbers of distribution parameters, I don't see much advantage in having the randomization done inside a function. It will probably save keystrokes just to generate them individually, e.g.,
sz=[100,1];
x1=randn(sz)*0.6 +3;
x2=poissrnd(7,sz);
...
x5=random('Stable',2,0,1,0,sz);
DV=fun([x1,x2,x3,x4,x5]);
I suppose you could try to do it all with the random() command:
s(1).args={'Normal',3,0.6};
s(2).args={'Poisson',7};
...
s(5).args={'Stable',2,0,1,0};
function DV = ProbInpEx(fun, M, s)
siz=[M,1];
xcell=arrayfun( @(i) random( s(i).args{:}, siz) , 1:numel(s),'uni',0);
DV=fun(xcell{:});
end

Sign in to comment.

Categories

Find more on Descriptive Statistics 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!