Avoiding eval in matlab function - matlab

I use the symbolic toolbox in matlab to generate some very long symbolic expressions. Then I use matlabFunction to generate a function file.
Say there are three parameters: p1, p2 and p3.
I have a cell with strings {'p1', 'p2', 'p3'}.
In the derivation of the model I generate symbolic variables p1, p2 and p3 out of them using eval in a loop and stack them in a vector par.
Then when in matlabFunction, I specify par as input.
Moreover, I save the cell string in a .mat file.
Then when I want to simulate this model, I can construct this parameter array using that cell of strings from the .mat file out of 30 available parameters and their values.
Advantages: No need to keep track of the different parameters if I add one to . I can change the order, mess around, but older models still work.
Disadvantage:
Turning things into a function file leads to this error (psi is one of the parameters):
Error: File: f_derive_model.m Line: 96 Column: 5
"psi" previously appeared to be used as a function or
command, conflicting with its use here as the name of a
variable.
A possible cause of this error is that you forgot to
initialize the variable, or you have initialized it
implicitly using load or eval.
Apparently some unnescescary checking is going on because the variable will be intialized in an eval statement.
Question: How can I avoid eval but keep the list of parameters indepent from the model stuff.
Code deriving the long equations:
% Model parameters
mdl.parameters = {'mp','mb','lp','lb','g','d','mP','mM','k','kt'};
par = [];
for i=1:length(mdl.parameters)
eval(strcat(mdl.parameters{i}, '=sym(''', mdl.parameters{i}, "');"));
eval(sprintf(['par = [par;' mdl.parameters{i} '];']));
end
%% Calculate stuff
matlabFunction(MM,'file',[modelName '_mass'],'vars',{par},'outputs',{'M'});
Code using the generated file:
getparams
load('m3d_1')
par = [];
for i=1:length(mdl.parameters)
eval(sprintf(['par = [par;params.' mdl.parameters{i} '];']));
end
See how, as long as I specify the correct value to for example params.mp, it always gets assigned to the input corresponding to the symbolic variable mp in the par vector. I do not want to lose that and have to keep track of the order and so on, nor do I want to call my functions with all the parameters one by one.

Actually, I see nothing wrong in your approach even if the "public opinion" affirms that it's better to avoid using the eval function. An alternative would be using the assignin function as follows:
% use 'caller' instead of 'base' if this code runs within a function
for i = 1:numel(mdl.parameters)
var_name = mdl.parameters{i};
assignin('base',var_name,sym(var_name));
end
In the second case (the one concerning the par variable) I would instead use the getfield function:
par_len = numel(mdl.parameters);
par = cell(par_len,1);
for i = 1:par_len
par{i} = getfield(params,mdl.parameters{i});
end
or, alternatively, this approach:
par_len = numel(mdl.parameters);
par = cell(par_len,1);
for i = 1:par_len
par{i} = params.(mdl.parameters{i});
end

Related

How do I get the value of a Simulink struct from the workspace within a MATLAB function?

I need to access the values of variables in MATLAB's workspace of type Simulink.parameter:
CAL_vars = dsdd('find','/path/CAL','ObjectKind','Variable','Property',{'name' 'Class' 'value' 'CAL'})
%gets ids of variables in data dictionary
i = 10
for i=1:length(CAL_vars)
var_name = dsdd('GetAttribute',CAL_vars(i),'name');
% gets names of variables in data dict
var_eval = eval(var_name); % this works in standalone script and it does exactly
% what I need, but once i put it in the function I need this for, it returns error
if (length(var_eval.Value) ==1)
if (var_eval.Value == true)
var_eval.Value = 1;
elseif (var_eval.Value == false)
var_eval.Value = 0;
else
end
end
% do something with the Value
if (errorCode ~= 0)
fprintf('\nSomething is wrong at %s\n', var_name)
end
end
The problem arises because the structs are of made by Simulink and give error, when I try to call eval(name_of_var): Undefined function 'eval' for input arguments of type 'Simulink.Parameter'.
Curiously, it seems to function properly in a stand-alone script but once I plug it into the larger function, it stops working and starts displaying error saying
Error using eval
Undefined function or variable 'name_of_var'.
The function is clearly in the workspace.
Curiously, it seems to function properly in a stand-alone script but
once I plug it into the larger function, it stops working
This is the expected behaviour. A function has its own workspace and can't directly access variables in the base workspace.
You could try using evalin instead of eval, and specify the base workspace:
evalin(ws, expression) executes expression, a character vector or
string scalar containing any valid MATLABĀ® expression using variables
in the workspace ws. ws can have a value of 'base' or 'caller' to
denote the MATLAB base workspace or the workspace of the caller
function.
In general though, there are lots of reasons for trying to avoid using eval if at all possible (see the MATLAB help for eval) and it would be best if you could find a different way of getting this data.

Passing multiple inputs into a MATLAB function via loop?

I have multiple variables var_1, var_2, var_3....var_9 (they are named like that) that I want to pass in a function. All of the variables are saved in the workspace. The function takes 2 variables, and spits out an output. I want to compare var_1 with all the variables, including itself, so I prefer to automate it in a loop.
So I want to execute
function(var_1,var_1)--> display answer, function(var_1,var_2)--> display answer...function(var_1,var_9)-->display answer all at once in a loop. I've tried the following, with no luck:
for i=1:7
functionname(var_1,var_'num2str(i)')
end
Where did I go wrong?
You cannot make a dynamic variable name directly. But you can use the eval-function to evaluate an expression as a string. The string can be generated with sprintf and replaces %d with your value.
for i=1:7
eval(sprintf('functionname(var_1,var_%d)', i));
end
But: Whenever you can, you should avoid using the eval function. A much better solution is to use a cell array for this purpose. In the documentation of Matlab there is a whole article about the why and possible alternatives. To make it short, here is the code that uses a cell array:
arr = {val_1, val_2, val_3, val_4, val_5, val_6, val_7, val_8, val_9};
for i = 1:length(arr)
functionname(arr{1},arr{i})
end

Matlab permutation

I have written a function to perform permutations of n obejects.
I have the variables a=[1],[2],[3];k=1 and n=4;a contains the objects which are 1,2 and 3 respectively.The following is the function code that i have written:
function [res]=perm(a,k,n,jj)
if k==n
res{jj}=a;
jj=jj+1;
else
for i=k:n
t=a{k};
a{k}=a{i};
a{i}=t;
perm(a,k+1,n,jj)
t=a{k};
a{k}=a{i};
a{i}=t;
end
end
end
However, when i call the function as:
jj=1;
[res]=perm(a,k,n,jj)
I am getting the following error displayed:
Error in ==> perm at 3
if k==n
??? Output argument "res" (and maybe others) not assigned during call to "J:\main
project\perm.m>perm".
Error in ==> mainp at 254
[res]=perm(a,k,n,jj)
The following is the code in the main program with regard to the permutation:
mr=4
for i=1:mr
a{i}=i;
end
n=mr;
%This assignment is for the ease to work with.
%just stored the indices till mr for the purpose of permutation
k=1;
%this is the k that the function perm has
jj=1;
[res]=perm(a,k,n,jj)
Can somebody please help me resolve this?Thanks in advance.
Your else block leaves res undefined.
I think you're assuming that res is a global variable and all invocations of perm will write into different parts of a single cell array. That isn't true. It is an output variable, local to the current call. There's no sharing during recursion, they all have independent cell arrays named res. The parameter jj is also not shared, so adding one is useless as well.
If you want to use this technique of building up the output, you'll need to make sure it is defined at a wider scope than the recursion. For example, use a local helper function:
function [res]=perm(a,k,n,jj)
res = {};
perm_impl(a,k);
function [] = perm_impl(a,k) // doesn't get its own local res, n, or jj
if k==n
res{jj}=a;
jj=jj+1;
else
for i=k:n
t=a{k};
a{k}=a{i};
a{i}=t;
perm_impl(a,k+1)
t=a{k};
a{k}=a{i};
a{i}=t;
end
end
end
end
Now all runs of perm_impl work on the same cell array res, because Matlab documentation says:
Variables within nested functions are accessible to more than just their immediate function. A variable, x, to which you assign a value or use within a nested function resides in the workspace of the outermost function that both contains the nested function and accesses x.
If you intentionally use a variable in this manner, it is not a problem. For examples, see the MATLAB Programming Demo on Nested Functions.
However, if you unintentionally use a variable in this manner, it can result in unexpected behavior. If the highlighting indicates that the scope of a variable spans multiple functions, and that was not your intent, consider:
Renaming the nested function variable so it does not match the outer function variable name.
Passing the variable into the function as an input argument instead of using the variable directly within the nested function
I can't tell whether a was supposed to be shared or not...

matlab: is there a way to import/promote variables from a structure to the current workspace?

function y = myfunc(param)
C = param.C;
L = param.L;
Kp = param.Kp;
Ki = param.Ki;
...
Is there a way to generalize the above code? I know how to generalize the structure access using fieldnames() and getfield(), but not how to set variables without calling eval() (which is evil).
for n = fieldnames(param)'
name = n{1};
value = param.(name);
do_something_with(name,value); % ????
never mind, I figured it out; this helper function works:
function vars_pull(s)
for n = fieldnames(s)'
name = n{1};
value = s.(name);
assignin('caller',name,value);
end
The only way to create a variable whose name is determined at run-time is to use a function like eval, evalin, feval, or assignin. (assignin is the least evil choice BTW, at least you don't need to convert your value to a string and back.)
However, I question why you want to do that, why not just access the values through the input structure as you need them. If you want to save typing (speaking from experience, as I am extremely lazy), I usually name my input parameter structure something short, like p. The throughout my code I just access the fields directly, (e.g. p.Kp, and after a while I don't even see the p. anymore.) This also makes it easy to pass the structure into subfunctions as needed.
You can use the excellent submission at FileExchange:
V2STRUCT - Pack & Unpack variables to & from structures with enhanced functionality
Here's a workaround: save the structure to a .mat file using the '-struct' option, and then immediately reload it. Here's an example for struct variable X:
save('deleteme.mat','-struct','X');
load('deleteme.mat');
delete('deleteme.mat');
It's kludgey, but actually pretty fast, at least with an SSD.

How can I move variables into and out of a structure akin to LOAD and SAVE in MATLAB?

Is there a quick way (i.e. one line) to dump a collection of variables "in" a structure, using the variable names as the structure fields? The "load" function basically does this but saving and loading to a temporary file seems ugly.
For example:
clear
a = 'adsf'
b = rand(10);
x = var2struct(a,b)
x.a
x.b
or better yet:
x = var2struct(['a';'b'])
Also, what about the reverse (i.e. dumping the field values to the current scope as variables named after the fields)?:
clear
x.a='asdf'
x.b=rand(10);
dumpstruct(x)
a
b
Also, here's a related newsgroup thread.
Aside from using LOAD and SAVE, there is no built-in function that I know of to do this. However, you could just make your own functions, like so:
function s = var2struct(varargin)
names = arrayfun(#inputname,1:nargin,'UniformOutput',false);
s = cell2struct(varargin,names,2);
end
function struct2var(s)
cellfun(#(n,v) assignin('base',n,v),fieldnames(s),struct2cell(s));
end
Working from the base workspace, you can use these functions like so:
a = 'adsf'
b = rand(10);
x = var2struct(a,b);
clear a b
struct2var(x);
A couple notes:
If you would rather specify the arguments to var2struct as the variable names instead of the variables themselves, here is an alternative function:
function s = var2struct(varargin)
values = cellfun(#(n) evalin('base',n),varargin,'UniformOutput',false);
s = cell2struct(values,varargin,2);
end
And you would use this from the base workspace as follows:
x = var2struct('a','b');
Unfortunately, you can only use this version of the function to get variables from the base workspace, not the workspace of a function.
One caveat with the struct2var function above is that it will always create the variables in the base workspace, not the workspace of the function calling struct2var. To create variables in a workspace other than the base, you would have to use this line in that workspace instead of calling struct2var:
cellfun(#(n,v) assignin('caller',n,v),fieldnames(x),struct2cell(x));