Saving All Results Of Objective Function in Genetic Algorithm

7 views (last 30 days)
Hi,there is an answered question about saving objective function value at each evaluation of genetic algorithm here;
https://www.mathworks.com/matlabcentral/answers/453836-how-can-i-store-the-value-at-each-iteration-of-a-genetic-algorithm
Stephan's answer is good solution for serial evaluation of objective function.
[x,fval,vals] = solve_problem
function [x,fval,vals] = solve_problem
ObjFcn = @myFitness;
nvars = 2; %number of variables
LB = [0 0]; %lower bound
UB = [1 13]; %upper bound
ConsFcn = @myConstraints;
iter = 1;
vals = [];
[x,fval] = ga(ObjFcn,nvars,[],[],[],[],LB,UB,ConsFcn);
function y = myFitness(x)
y = 100*(x(1)^2-x(2))^2 + (1-x(1))^2;
vals(iter,1:2) = x;
iter = iter+1;
end
function [c,c_eq] = myConstraints(x)
c = [x(1)*x(2) + x(1) - x(2) + 1.5; 10 - x(1)*x(2)];
c_eq = [];
end
end
But im trying to parallel evaluation of objective function. My objective function is an external objfunc.m file that calls an external program. In this case "vals" matrix suggested by Stephan doesnt work since "iter" cannot have a value in squence. Every worker tries to increase iter number simultaniously.
In my case;
parpool('AttachedFiles',{'objfunc.m'});
options = optimoptions(options,'UseParallel', true);
i send objfunc.m file to all worker first. So, all results coming simultaneously from each worker must be combined in a single matrix according to ascending values of "iter".
I need to save all x and fval values in the same row for each function evaluation according to results coming from workers.
How could be the solution in the parallel case?
Thank you

Accepted Answer

Dana
Dana on 26 Aug 2020
I'm not sure it's possible to do exactly what you've indicated, but I think you should be able to get something along those lines as follows. There might be a better way to do this, but one way that I think should work is to use persistent variables to store x and fval across calls of objfunc done by each individual parallel worker, and then retrieve them all at the end using an spmd command.
So your objfunc.m function should look something like:
function [fv,x2,fv2] = objfunc(x)
persistent xstore fvstore
fval = ExternalProgramFunction(x); % call your external program
% add the current x and fval to the persistent variables
xstore = [xstore; x]; % assuming x is a row vector
fvstore = [fvstore; fval];
% When optimizing, you won't need to recover the stored values. To
% save on unnecessary copying, only create copies of these variables
% for output if actually necessary.
if nargout > 1
x2 = xstore;
if nargout > 2
fv2 = fvstore;
end
end
end
Note that xstore and fvstore here are variables that will be shared by successive calls to objfunc by a particular worker, but not across different workers. So if you have 8 workers, then at the end of your optimization you'll have 8 different versions of xstore and fvstore. You can then retrieve them using spmd via
spmd
[~,x2,fv2] = objfunc(x);
end
clear objfunc
Here, x2 and fv2 will be composite variables with dimension equal to the number of parallel workers you have, and where x2{j} and fv2{j} returning the values of xstore and fvstore on worker j. Thus, the i-th rows of xj=x2{j} and fvj=fv2{j} will give the values of x and fval for the ith call of objfunc made by the j-th worker.
Note that the last line above ("clear objfunc") clears the persistent variables from memory. If you don't do that, then next time objfunc is called you'll continue to add rows to the stored variables, which you may not want. In general, it's a good idea to run this clear statement right after you've retrieved your data in order to avoid unexpected results later on.
Note also that it's not possible to group the calls made by the different workers by the corresponding genetic algorithm "generation". If that's what you're after, I'm not sure how to do that.
One final note. The above code, as well as the method you linked to for serial execution, involve expanding the size of the stored arrays at every iteration, which is a costly step to execute and should be avoided if possible. If you have some idea of what the maximum number of function calls will be, you can potentially speed things up by pre-allocating the xstore and fvstore arrays as follows:
function [fv,x2,fv2] = objfunc(x)
persistent xstore fvstore iter
if isempty(iter) % if this is the first call, initialize variables
maxcalls = 1000; % maximum number of calls you expect a worker to make
nx = 10; % number of elements of x
xstore = zeros(maxcalls,nx);
fvstore = zeros(maxcalls,1);
iter = 1;
else
iter = iter+1
end
fval = ExternalProgramFunction(x); % call your external program
% add the current x and fval to the persistent variables
xstore(iter,:) = x; % assuming x is a row vector
fvstore(iter) = fval;
% When optimizing, you won't need to recover the stored values. To
% save on unnecessary copying, only create copies of these variables
% for output if actually necessary.
if nargout > 1
x2 = xstore;
if nargout > 2
fv2 = fvstore;
end
end
end
  3 Comments
Dana
Dana on 27 Aug 2020
In your test you're using iter as a numeric counter, but then you're converting it to a string, and on the next call to the function you're adding 1 to it. If you try and add 1 to a string, you're going to get an unexpected output. To see what I mean, try the following in the command window:
>> iter=1
iter =
1
>> iter=num2str(iter)
iter =
'1'
>> iter=iter+1
iter =
50
>> iter=num2str(iter)
iter =
'50'
>> iter=iter+1
iter =
54 49
Hopefully you can see the problem. When you convert iter to a string the first time, MATLAB internally represents the character '1' as an integer character code, in this case 49 (you can check that char(49)='1'). When you then do iter=iter+1, the result is that integer character code + 1, i.e., 50. When you then convert that to a string to get '50' and then add 1 to it, MATLAB now increments each of the character codes in that string. Since '5'=char(53) and '0'=char(48), when you add 1 you now get a two-element vector [54, 49].
So long story short, this isn't what you want. Don't convert iter, instead use a different variable for the string representation:
persistent iter
if isempty(iter)
iter = 1
else
iter = iter+1
end
iterstring=num2str(iter);
TableName=strcat(worker,'_',iterstring)
save(TableName, 'DecisionVars');
Ugur Acar
Ugur Acar on 28 Aug 2020
Thank you Dana, as you said i used different variable for the string representation and it is done. Now it is time to communication between workers :)

Sign in to comment.

More Answers (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!