I have a handle class:
classdef A<handle
properties
a;
end
methods
function obj = A(a)
obj.a=a;
end
end
end
And I have a cell array of A objects:
arr={};
for i=1:3
arr{i}=A(i);
end
What I would like to do is pass that cell array to a parfor loop so that each object's value will change:
parfor i=1:3
arr{i}.a=i*5;
end
However, this code does not change arr at all. Indeed, here it states that
Changes made to handle classes on the workers during loop iterations are not automatically propagated to the client.
How can I overcome this?
An interesting question; I actually never encountered this problem. It's always good to know everything about parfor limitations, so I did some googlin' and came up with this:
I have received an answer to this issue from technical support.
Apparently the Mathworks regard it as a 'feature' that changes to
objects are not returned - though I can't see that it's a very useful
feature. Anyway, the way to get modified class properties returned
from a parfor loop is to make an explicit change which can be
recognised by parfor. Here are two examples which work for the above
example object:
parfor n = 1:num
exArray(n).data = n:n+5;
end
or
parfor n = 1:num
temp = exArray(n);
setData(temp,n:n+5);
exArray(n) = temp;
end
Actually, if you change any object property it also seems to work. So
for example this also works, if there is a second property data2 which
is set explicitly, both data and data2 are correctly returned:
parfor n = 1:num
setData(exArray(n),n:n+5);
exArray(n).data2 = n:n+5;
end
Where the example object is given by
classdef Example < handle
properties
data
end
methods
function obj = Example(data)
obj.data = data;
end
function setData(obj,data)
obj.data = data;
end
function data = getData(obj)
data = obj.data;
end
end
end
and the array is initialized simply as
% Initialise array of objects
for n = 1:num
exArray(n) = Example(zeros(1,6));
end
Related
In R2018b, consider the following toy class:
classdef MyObj < handle
properties
use_parallel = true
test_value = NaN
end
methods
function myMethod(obj)
% Call one of the nested functions below:
if all([obj.use_parallel])
parallel();
disp('Parallel (inside myMethod):')
[obj.test_value]
else
sequential();
disp('Sequential (inside myMethod):')
[obj.test_value]
end
% Sequentially assign some values
function sequential()
for ii = 1:numel(obj)
obj(ii).test_value = ii; end
end
% Assign some values in parallel
function parallel()
parfor ii = 1:numel(obj)
set_value(obj(ii),labindex());
obj_copy(ii) = obj(ii);
end
obj = obj_copy;
end
end
end
end
% parfor requires subfunction (and not nested function):
function set_value(obj,index)
obj.test_value = index;
end
Though this question closely resembles this and this question, their underlying problem was essentially some variant of the limitation outlined in the documentation:
You can send handle objects as inputs to the body of a parfor-loop. However, any changes made to handle objects on the workers during loop iterations are not automatically propagated back to the client. That is, changes made inside the loop are not automatically reflected after the loop
However, as far as I can see, the toy class above is compliant with the parfor slicing rules as well as these particulars regarding handle classes. In my understanding, it should therefore correctly copy the modified obj back to myMethod's workspace.
However, running the following:
clc
% Assign sequentially
M(3) = MyObj();
[M.use_parallel] = deal(false);
M.myMethod();
disp('Sequential (outside class):')
[M.test_value]
disp(' ')
% Assign in parallel
N(3) = MyObj();
[N.use_parallel] = deal(true);
N.myMethod();
disp('Parallel (outside class):')
[N.test_value]
gives on my parpool of 6 workers:
Sequential (inside myMethod):
ans =
1 2 3 % <- OK
Sequential (outside class):
ans =
1 2 3 % <- OK. Nothing unexpected
Parallel (inside myMethod):
ans =
1 1 1 % <- OK, apparently, lab 1 did everything
Parallel (outside class):
ans =
NaN NaN NaN % <- hmmm...changes did not propagate
This means the obj.test_value gets correctly assigned, and the modified obj is indeed correctly copied into myMethod's workspace. Yet somehow, this modified obj is a different entity than the obj before modification, because the changes do not propagate higher up the stack...
Changing the parallel() function to a subfunction (instead of a nested function) and explicitly passing the obj parameter around, does not affect this outcome.
Sooooo...what's going on here?
I can reduce the problem to this:
classdef MyObj < matlab.mixin.Copyable
properties
test_value = NaN
end
methods
function myMethod(obj)
obj = obj.copy();
obj.test_value = rand;
disp('Inside method:')
obj.test_value
end
end
end
Removing nested functions, subfunctions and parfor from the equation. Running the code (after obvious changes) above results in:
Inside method:
ans =
4.8089e-01 % <- obj re-initialized OK
Outside class:
ans =
NaN % <- but this does NOT modify the handle class!
That means it's a language "feature" I wasn't aware of! Apparently, methods can modify existing objects, but not redefine them completely. I'll try digging in the documentation to find out some more about this, but this poses a whole new problem: how to copy the changes made by parfor back into the object?
TL;DR:
function myMethod(obj)
% ↓ new object ↓ old object
obj = obj.copy();
Cris was right; though the new and old objects have identical names, they are different things—they shadow each other. Any changes made to the new obj will not modify the old obj.
I'm trying to dynamically fill a property in a Matlab class.
I pass vectors to a method function and then compute various parameters. I would like to fill the properties in a for loop, see code example. The OwnClassFunction is just an example of a further function in the class, but is not implemented in the code example. How can I do this correctly?
classdef Sicherung < handle
properties
x = ([],1)
end
methods
function examplefunction(object,...
single_parameter_vector) % (n,1) n can be any size
for i=1:length(param_vector)
[object.x(i,1)] = object.OwnClassFunction(single_parameter_vector(i,1));
end
end
end
end
If i try something like that
...
properties
x = []
end
...
function ...(object,parameter)
for i=1:length(parameter)
[object.x(i)] = function(parameter(i));
end
I get the error message Subscripted assignment dimension mismatch.
I don’t have MATLAB in hand to test, but the following should work.
Your code is pretty close to a correctly functioning method. Change it as follows:
classdef Sicherung < handle
properties
x = [] % initialize to empty array
end
methods
function examplefunction(object,param_vector)
if ~isvector(param_vector)
error('vector expected') % check input
end
n = numel(param_vector)
object.x = zeros(n,1); % preallocate
for i=1:n
object.x(i) = object.OwnClassFunction(param_vector(i));
end
end
end
end
To all,
I'm a novice programmer (I haven't taken a structured course) trying to pick up MATLAB. Currently, I'm trying to understand MATLAB's object-oriented programming tools. I want to cleanly divide up my project in the steps below. I would like to inherit the substantiated data and work on it in validate. I'm running into this error You cannot set the read-only property 'data' of validations.
How would I go about doing this? Would this be the appropriate way to organize myself in MATLAB?
classdef formatData.m
properties
data = []
end
methods
function formatData(data)
% This should initialize the data
end
function columnShift(data)
% Simple changes
end
end
end
classdef validateData.m < formatData
properties
error = logical(false)
end
methods
function validateData(data)
if nargin > 0
obj.data = obj#formatData;
end
end
function checkValues()
% check values on data from formatData
end
end
end
The sad new is that MATLAB often changes the object-oriented codes. The code that I have posted is referenced from the latest documents. It seems completely different with a book that I have owned for one year.
classdef formatData
properties
data = [];
end
methods
function obj = formatData(DataIn)
%formatData This should initialize the data
obj.data = DataIn;
end
function [DataOut] = columnShift(obj)
%columnShift ? How to shift the column?
DataOut = (obj.data)';
end
end
end
classdef validateData < formatData
%validateData
properties
error = false;
end
methods
function obj = validateData(DataIn)
%validateData
obj#formatData(DataIn);
end
function [] = checkValues(obj)
%checkValues
end
end
end
I am a beginner in matlab and learn about the following question online and was having trouble solving it. I have a 1 x 20 matrix called current_load that I need to update periodically. This matrix resides in the main workspace of Matlab (as shown in the code bellow).
current_loads = zeros(1, 20);
for col=1:20
current_loads(1,col)=10; %// Initially give all nodes a current value of 10
end
Object = HandleObject(current_load);%To pass by reference
recursive_remove(Object, 0);
In order to pass current_load by reference, I have created the following class HandleObject.m
classdef HandleObject < handle
properties
Object=[];
end
methods
function obj=HandleObject(receivedObject)
obj.Object=receivedObject;
end
end
end
This matrix will be passed to a function call recursive_remove (shown bellow).
function recursive_remove( current_load, val )
current_load.object = new matrix;
if(val<10)
current_load.object(1,3) = 2+val; %Not the correct way of using current_load ??
end
recursive_remove( current_load, current_load.object (1,3) )
end
Intention here is to modify current_load variable in this function and later I can see these same changes from the main. But This code doesn't work since I don't know how to pass by reference. I need to pass by reference since I am calling this recursively without returning to the main to make it overwrite its variable at the caller. Please show with an example if possible.
If you really need this feature, you can look into turning your HandleObject class into a Singleton like this:
classdef HandleObject < handle
properties
Object=[];
end
methods(Static)
function obj = Instance(receivedObject)
persistent uniqueInstance
try
if isempty(uniqueInstance)
obj = HandleObject(receivedObject);
uniqueInstance = obj;
else
obj = uniqueInstance;
end
catch ME
disp(ME.getReport);
end
end
end
methods
function obj=HandleObject(receivedObject)
obj.Object=receivedObject;
end
end
end
Your recursive function would become a bit simpler then:
function recursive_remove(val )
current_load = HandleObject.Instance();
current_load.object = new matrix;
if(val<10)
current_load.object(1,3) = 2+val;
end
recursive_remove(current_load.object (1,3) )
end
And to create an instance of your HandleObject class, you do something like:
Object = HandleObject.Instance(current_load);
Hope this helps you further.
I am having trouble using struct arrays in Matlab's parfor loop. The following code has 2 problems I do not understand:
s=struct('a',{},'b',{});
if matlabpool('size')==0
matlabpool open local 2
end
for j = 1:2
parfor k=1:4
fprintf('[%d,%d]\n',k,j)
s(j,k).a = k;
s(j,k).b = j;
end
end
matlabpool close
It fails with an error Error using parallel_function (line 589)
Insufficient number of outputs from right hand side of equal sign to satisfy assignment.
On output, variable s is a vector, not an array (as it should be, even if the code breaks before finishing).
EDIT the problem is solved if I initialize the struct arrays to the correct size, by:
s=struct('a',cell(2,4),'b',cell(2,4));
However, I would still be happy to get insights about the problem (e.g is it rally a bug, as suggested by Oleg Komarov)
It was originally working fine for me but then I don't know what happens. In general you need to be careful with parfor loops and there are ample documentation on how to align everything. Two different words of advice.
First and more importantly, the parfor loop is on the outside loop:
function s = foo
s=struct('a',{},'b',{});
parfor j = 1:2
for k=1:4
fprintf('[%d,%d]\n',k,j)
s(j,k).a = k;
s(j,k).b = j;
end
end
Two, Matlab gets very picky about writing the main exit variable (i.e. the variable contained in the parfor loop which is indexed to the loop, in your case, s). You first want to create a dummy variable that holds all the innerloop information, and then writes to it once at the end of the loops. Example:
function s = khal
s=struct('a',{},'b',{});
parfor j = 1:2
dummy=struct('a',{},'b',{});
for k=1:4
fprintf('[%d,%d]\n',k,j)
dummy(k).a = k;
dummy(k).b = j;
end
s(j,:) = dummy;
end
You don't have a problem here, but it can get complicated in other instances