So I am trying to optimize 2 set parameters one for each axle of this scheme.The first set has to be optimized on delta between left and right flywheel, while set 2 is the same for the other axle
At the moment I am using fmincon and running in parallel the simulation of left and right flywheel of each axle as the cost function is the error between the two outputs.
The basic code is something like this
for Axle = 1:2
...
fmincon(CostFunc,InitialValues, lb,ub)
end
The CostFunc is something like this
fob = CostFunc(...)
parfor FlyWheel = 1:2
FlyWheelOutput(FlyWheel) = simulation(...)
end
fob = FlyWheelOutput(1) - FlyWheelOutput(2)
So instead of 4 cores I a using only 2, making the code slower than what it could ideally be. So the question is do you know anyway how to overcompe this impasse?
Basic scheme
If my understanding is correct, your code effectively has this basic structure:
for Axle = 1:2
...
% fmincon(Costfunc, ...)
parfor FlyWheel = 1:2
FlyWheelOutput(FlyWheel) = simulation(...)
end
% end fmincon
end
You could potentially speed up processing by moving parfor to the outer loop instead.
parfor Axle = 1:2
...
% fmincon(Costfunc, ...)
for FlyWheel = 1:2
FlyWheelOutput(FlyWheel) = simulation(...)
end
% end fmincon
end
While I can't guarantee that you will use all 4 CPU cores simultaneously instead of 2, this could be faster, since you are executing parfor once outside a loop, instead of twice inside a loop.
If runtime is still a problem, consider using the built-in profiling tool to find out which part of your code is slowest, then optimize from there.
Related
I am trying to use a for loop inside of a parfor loop in Matlab.
The for loop is equivalent to the ballode example in here.
Inside the for loop a function ballBouncing is called which is a system of 6 differential equations.
So, what I am trying to do is to use 500 different sets of parameter values for the ODE system and run it, but for each parameter set, a sudden impulse is added, which is handled through the code in 'for' loop.
However, I don't understand how to implement this using a parfor and a for loop as below.
I could run this code by using two for loops but when the outer loop is made to be a parfor it gives the errors,
the PARFOR loop cannot run due to the way variable results is used,
the PARFOR loop cannot run due to the way variable y0 is used and
Valid indices for results are restricted in PARFOR loops
results=NaN(500,100);
x=rand(500,10);
parfor j=1:500
bouncingTimes=[10,50];%at time 10 a sudden impulse is added
refine=2;
tout=0;
yout=y0;%initial conditions of ODE system
paras=x(j,:);%parameter values for the ODE
for i=1:2
tfinal=bouncingTimes(i);
[t,y]=ode45(#(t,y)ballBouncing(t,y,paras),tstart:1:tfinal,y0,options);
nt=length(t);
tout=[tout;t(2:nt)];
yout=[yout;y(2:nt,:)];
y0(1:5)=y(nt,1:5);%updating initial conditions with the impulse
y0(6)=y(nt,6)+paras(j,10);
options = odeset(options,'InitialStep',t(nt)-t(nt-refine),...
'MaxStep',t(nt)-t(1));
tstart =t(nt);
end
numRows=length(yout(:,1));
results(1:numRows,j)=yout(:,1);
end
results;
Can someone help me to implement this using a parfor outer loop.
Fixing the assignment into results is relatively straightforward - what you need to do is ensure you always assign a whole column. Here's how I would do that:
% We will always need the full size of results in dimension 1
numRows = size(results, 1);
parfor j = ...
yout = ...; % variable size
yout(end:numRows, :) = NaN; % Expand if necessary
results(:, j) = yout(1:numRows, 1); % Shrink 'yout' if necessary
end
However, y0 is harder to deal with - the iterations of your parfor loop are not order-independent because of the way you're passing information from one iteration to the next. parfor can only handle loops where the iterations are order-independent.
Suppose I have two functions written on different scripts, say function1.m and function2.m The two computation in the two functions are independent (Some inputs may be the same, say function1(x,y) and function2(x,z) for example). However, running sequentially, say ret1 = function1(x,y); ret2 = function2(x,z); may be time consuming. I wonder if it is possible to run it in parfor loop:
parfor i = 1:2
ret(i) = run(['function' num2str(i)]); % if i=1,ret(1)=function1 and i=2, ret(2)=function2
end
Is it possible to write it in parfor loop?
Your idea is correct, but the implementation is wrong.
Matlab won't let you use run within parfor as it can't make sure it's a valid way to use parfor (i.e. no dependencies between iterations). The proper way to do that is to use functions (and not scrips) and an if statement to choose between them:
ret = zeros(2,1);
parfor k = 1:2
if k==1, ret(k) = f1(x,y); end
if k==2, ret(k) = f2(x,z); end
end
here f1 and f2 are some functions that return a scalar value (so it's suitable for ret(k) and each instance of the loop call a different if statement.
You can read here more about how to convert scripts to functions.
The rule of thumb for a parfor loop is that each iteration must be standalone. More accurately,
The body of the parfor-loop must be independent. One loop iteration
cannot depend on a previous iteration, because the iterations are
executed in a nondeterministic order.
That means that every iteration must be one which can be performed on its own and produce the correct result.
Therefore, if you have code that says, for instance,
parfor (i = 1:2)
function1(iterator,someNumber);
function2(iterator,someNumber);
end
there should be no issue with applying parfor.
However, if you have code that says, for instance,
persistentValue = 0;
parfor (i = 1:2)
persistentValue = persistentValue + function1(iterator,someNumber);
function2(iterator,persistentValue);
end
it would not be usable.
Yes. It is possible.
Here's an example:
ret = zeros(2,1);
fHandles = {#min, #max};
x = 1:10;
parfor i=1:2
ret(i) = fHandles{i}(x);
end
ret % show the results.
Whether this is a good idea or not, I don't know. There is overhead to setting up the parallel processing that may or may not make it worthwhile for you.
Typically the more iterations you have computed, the more value you get from setting up a parfor loop as the iterations are sliced-up and sent non-deterministically to the separate cores for processing. So you're getting use of 2 cores right now, but if you have many functions this may improve things.
The order that the iterations are run is not guaranteed (it could be that one core gets assigned a range of values for i, but we do not know if it those values are taken in order or randomly), so your code can't depend on other iterations of the loop.
In general, the MATLAB editor is pretty at flagging these issues ahead of time.
EDIT
Here's a proof of concept for a variable number of arguments to your different functions
ret = zeros(2,1);
fHandles = {#min, #max};
x = 1:10; % x is a 1x10 vector
y = rand(20); % y is a 20x20 matrix
z = 1; % z is a scalar value
fArgs = {{x};
{y,z}}; %wrap your arguments up in a cell
parfor i=1:2
ret(i) = fHandles{i}([fArgs{i}{:}]); %calls the function with its variable sized arguments here
end
ret % show the output
Again, this is just proof-of-concept. There are big warnings showing up in MATLAB about having to broadcast fArgs across all of the cores.
The following code works, but if I change for into parfor, it gave an error
Index exceeds matrix dimensions
This is my code
a=zeros(3,1);
for t=1:2
ind=randsample(3,2)
a=pf(a,ind)
end
function a=pf(a,ind)
a(ind)=a(ind)+2;
end
How can I get this code working without the error?
You are seeing the error because you are misusing parfor in your code. You haven't read the relevant documentation enough, and you seem to believe that parfor is magic fairy dust that makes your computation faster, regardless of computation. Well, I have bad news.
Let's take a closer look at your example:
a = zeros(3,1);
% usual for
disp('before for')
for t=1:2
ind = randsample(3,2);
a = pf(a,ind);
disp(a); % add printing line
end
% parfor
disp('before parfor')
parfor t=1:2
ind = randsample(3,2);
a = pf(a,ind);
disp(a); % add printing line
end
The output:
before for
2
2
0
2
4
2
before parfor
Error: The variable a is perhaps intended as a reduction variable, but is actually an uninitialized temporary.
See Parallel for Loops in MATLAB, "Temporary Variables Intended as Reduction Variables".
As you can see, in the latter case there are no prints inside the parfor, so it doesn't even get run. See also the warning about the type of variables. The variable a is being misidentified by the execution engine because what you are doing to it doesn't make any sense.
So what to do instead? You need to formulate your problem in a way that is compatible with parfor. This will, alas, depend on what exactly you're doing to your matrix. For your specific case of incrementing random elements, I suggest that you gather the increments separately in the loop, and sum them up afterwards:
a = zeros(3,1); % only needed for size; assumed that it exists already
numiters = 2;
increments = zeros([size(a), numiters]); % compatible with a proper 2d array too
parfor t=1:numiters
ind = randsample(3,2);
% create an auxiliary increment array so that we can use a full slice of 'increments'
new_contrib = zeros(size(a));
new_contrib(ind) = 2;
increments(:,t) = new_contrib;
disp(increments(:,t)); % add printing line
end
% collect increments along last axis
a = sum(increments,ndims(increments));
disp(a)
Output:
2
0
2
2
2
0
4
2
2
Note the lack of warnings and the presence of a meaningful answer. Refactoring the loop this way transparently signals MATLAB what the variables are doing, and that increments is being filled up by independent iterations of the parfor loop. This is the way in which parfor can "speed up calculations", a very specific and controlled way that implies restrictions on the logistics used inside the loop.
n = 2;
a=zeros(3,1);
ind=zeros(3,2,n);
for ii = 1:n
ind(:,:,ii) = randsample(3,2);
end
for t=1:n
a=pf(a,ind(:,:,t));
end
function a=pf(a,ind)
a(ind)=a(ind)+2;
end
The above gets the randsample out of the loop, which is probably the issue here. Note that randsample does not support direct 3D matrix creation, so I initialised that in a loop.
I am using parfor for parallel computing in Matlab. I am not familiar with this command. If that is possible, please look at my code below and tell me if I can write it with parfor.
These errors and warnings are appear in Matlab Editor:
The parfor loop cannot be run due to the way variable Dat is used. (when I comment line Dat.normXpj = normXpj(pj,:); This error is solved and other errors similar to the following error is appeared.
The entire array or structure Bound is broadcast variable. This
might result in unnecessary communication overhead.
parfor pj = 1:size(normXpj,1)
Dat.normXpj = normXpj(pj,:);
if size(Dat.InitialGuess)==0
X = (Bound(:,1)+(Bound(:,2)-Bound(:,1)).*rand(Nvar,1))';
else
X = Dat.InitialGuess;
end
[Xsqp, ~, FLAG,Options] = mopOPT(X,Dat);
FEVALS = Options.funcCount;
FES = FES+FEVALS;
PSet(pj,:) = Xsqp;
PFront(pj,:) = mop(Xsqp,Dat,0);
if FLAG==-2
disp('.......... Algo paso...');
else
F = PFront(pj,:);
if Nobj==2
plot(F(1,1),F(1,2),'*r'); grid on; hold on;
elseif Nobj==3
end
end
end
The problem here is that it we can see that you're not using Dat in a way that is order-dependent, but the static analysis machinery of parfor cannot deduce that because of the way you're assigning into it. I think you can work around this by instead creating a whole new Dat for each iteration of the loop, like so:
Dat = struct('normXpj', rand(10,1), 'InitialGuess', 3);
normXpj = rand(10);
parfor idx = 1:10
tmpDat = struct('normXpj', normXpj(:,idx), 'InitialGuess', Dat.InitialGuess);
% use 'tmpDat'
disp(tmpDat);
end
The answer is no, unfortunately. At line:
Dat.normXpj = normXpj(pj,:);
you assign a value to Dat.normXpj, but you have to know that in a parfor loop there can be multiple iterations executing at the same time. So what value should be used for Dat.normXpj ? Matlab cannot decide, hence your error.
More generally, your code looks quite messy. I suppose you want to use parfor to increase execution speed. Probably a more efficient option would be to use the profiler (see profile) to detect the bottlenecks in your code, and apply a correction if that's possible.
Best,
I have a basic code like this :
parfor i=1:8
[t,y]=ode15s(#rate,tspan,cin,options,i); % the option i is evaluated in the rate function
figure(1)
subplot(3,3,i+1)
plot(t,y)
hold on
end
Will any conflict arise because the variable name y is same in all iterations ?
No, every worker has its unique namespace.
However, a worker cannot open figures that display on the client (thanks for the reminder, #Edric), so everything after the call to ode15s will not produce any useful result.
To move the plotting outside the parfor loop, you can do the following (there are more efficient solutions, this one will work for sure):
tCell = cell(8,1);
yCell = cell(8,1);
parfor i=1:8
[tCell{i},yCell{i}]=ode15s(#rate,tspan,cin,options,i); % the option i is evaluated in the rate function
end
figure(1)
for i=1:8
subplot(3,3,i+1)
plot(tCell{i},yCell{i})
hold on
end
Following on from #Jonas' answer, just a note to point out that if you're using R2013b or later, and you wish to display graphics while waiting for your parallel computations to complete, you could use PARFEVAL, like in this example: http://www.mathworks.co.uk/help/distcomp/examples/parfeval-blackjack.html .