2
votes

I am reading the Matlab user's guide of optimization toolbox. In page 1-15, some codes are provided for creating variables for indexing. Here is the code:

%Combine variables into one vector
variables = {'I1','I2','HE1','HE2','LE1','LE2','C','BF1',...
'BF2','HPS','MPS','LPS','P1','P2','PP','EP'};
N = length(variables);
% create variables for indexing
for v = 1:N
    eval([variables{v},' = ',num2str(v),';']);  %?
end

I know the class of "variables" is cell array. But I cannot understand clearly the function of "eval". To read the following code, It seems to create the index for the elements in variables, so that the elements can be used as the index number used for manipulating matrix or vector. for example:

lb = zeros(size(variables));
lb([P1,P2,MPS,LPS]) = [2500,3000,271536,100623];

I have read the help document, but still cannot get it. So, anyone can explain for me more clearly.

by the way, user's guide suggests avoiding this "eval" function. So, is there any other way to achieve the above feature?

Thanks all

The complete program

% Combine variables into one vector
variables = {'I1','I2','HE1','HE2','LE1','LE2','C','BF1',...
   'BF2','HPS','MPS','LPS','P1','P2','PP','EP'};
N = length(variables);
% create variables for indexing
for v = 1:N
   eval([variables{v},' = ',num2str(v),';']);  %?
end


% Write bound constraints
lb = zeros(size(variables));
lb([P1,P2,MPS,LPS]) = ...
    [2500,3000,271536,100623];

ub = Inf(size(variables));
ub([P1,P2,I1,I2,C,LE2]) = ...
    [6250,9000,192000,244000,62000,142000];

% Write linear inequality constraints
A = zeros(3,N);
A(1,I1) = 1; A(1,HE1) = -1; b(1) = 132000;
A(2,EP) = -1; A(2,PP) = -1; b(2) = -12000;
A(3,[P1,P2,PP]) = [-1,-1,-1]; b(3) = -24550;

% Write linear equality constraints
Aeq = zeros(8,N); beq = zeros(8,1);
Aeq(1,[LE2,HE2,I2]) = [1,1,-1];
Aeq(2,[LE1,LE2,BF2,LPS]) = [1,1,1,-1];
Aeq(3,[I1,I2,BF1,HPS]) = [1,1,1,-1];
Aeq(4,[C,MPS,LPS,HPS]) = [1,1,1,-1];
Aeq(5,[LE1,HE1,C,I1]) = [1,1,1,-1];
Aeq(6,[HE1,HE2,BF1,BF2,MPS]) = [1,1,1,-1,-1];
Aeq(7,[HE1,LE1,C,P1,I1]) = [1267.8,1251.4,192,3413,-1359.8];
Aeq(8,[HE2,LE2,P2,I2]) = [1267.8,1251.4,3413,-1359.8];

% Write the objectvie
f = zeros(size(variables));
f([HPS PP EP]) = [0.002614 0.0239 0.009825];

% Solve the problem
%print out the results in floating-point fromat in a field 12     characters 
%wide, including 2 digits after the decimal point for first data
[x,fval] = linprog(f,A,b,Aeq,beq,lb,ub);
for d = 1:N
    fprintf('%12.2f \t %s \n',x(d),variables{d}); 
end
fval
1
For those wondering, yes this is in the official documentation. - excaza
Yes, it is. Maybe I didn't express myself obviously. Put another way, " eval" is to assign values to the elements in cell array here. is that right? If so, is there any other way to do the same thing? - Rolf_Zhang
@Rolf_Zhang sorry for leaving you hanging here with no answer: some of us discussed your question and got distracted by the shock caused by this horrible monster being present in the official documentation:) - Andras Deak
I changed the tags on your question: the [matlab-guide] which you used refers to GUIDE, the GUI design environment of MATLAB (you generally shouldn't use any tags that are related to guides or tutorials). - Andras Deak

1 Answers

6
votes

A few of us discussed your question and we're still in shock from the fact that this horrifying monster is in the official documentation:) What that example is doing is an obscene anti-pattern, that is not only unsafe but also highly inefficient. Your suspicions are correct, one should almost never use eval. When possible, one should do the job without eval and dynamic variable names. When it is not possible, one should refactor whatever code they are facing so that it can be solved in a nice, safe, fast and idiomatic way.

The problem here is that the construction itself mandates the use of eval. This is bad. Very bad. So bad that I hardly believed my own eyes when I saw this in the documentation. See this answer and references therein for why eval should be avoided like the plague. Generally speaking, eval executes arbitrary strings which poses a potential entry point for attackers, but honestly most use cases are not accessible to outsiders. However, just-in-time compiling in MATLAB can't optimize anything that is inside dynamic code. Finally, starting to work with dynamic variable names will lead you down an eval rabbit hole from where it is hard to escape.

So, what is the usual alternative to eval, especially in terms of dynamic field names? Cells, or more importantly, structs. I prefer the latter. The main obstacle in making people use structs instead of dynamic variable names is that there's a not-too-widely known feature of structs known as dynamic access of field names. The following two are identical:

% static version
mystruct1 = [];
mystruct1.field1 = 3;

% dynamic version
fname = 'field1';
mystruct2 = [];
mystruct2.(fname) = 3;

isequal(mystruct1,mystruct2)
% yes

So the usual solution to eval problems is using structs with dynamic field names.1

In your case this admittedly leads to difficulties. The notation will get more cumbersome, understandably. But in principle you could ditch the call to eval, and instead set the fields of a single index struct is:

is = [];
for v = 1:N
   % nope eval nope nope nope nope
   is.(variables{v}) = v;
end

The cost is that you have to be a bit less concise later:

Aeq(6,[is.HE1,is.HE2,is.BF1,is.BF2,is.MPS]) = [1,1,1,-1,-1];

I understand that you're reluctant to do so, and the toolbox that suggests using eval this way might hold other surprises for you, but I would probably take this route. The sake of one's mental hygiene and avoiding horrible anti-patterns should be highly motivating.

 

1this also implies that there's a way out from an eval rabbit hole: save your workspace as a .mat file, then load it with dat = load('tmp.mat');: the result will be a struct dat that you can readily access in the way you need to.