3 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

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

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');

Opportunities for recent engineering grads.

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

Start Hunting!
## 0 Comments

Sign in to comment.