Vectorization of multi-level indexing of structs in MATLAB - matlab

Say I have the following in MATLAB:
a(1).b.c = 4;
a(2).b.c = 5;
a(3).b.c = 7;
....
I would like to collect the values [4 5 7 ...] in a single array, without looping and in a vectorized manner.
I have tried:
>> a(:).b.c
# Error: Scalar index required for this type of multi-level indexing.
and
>> a.b.c
# Error: Dot name reference on non-scalar structure.
but they didn't work. The best I could come up with was:
arrayfun(#(x) x.b.c, a);
but as far as I understand arrayfun is not vectorized, or is it?

Your call to arrayfun seems idiomatic enough to me in Matlab. I don't think this is vectorized but it's well optimized and maybe the fastest way.
You should also try to benchmark with a loop to see if the JIT compiler performs well here. It's hard to know without testing.

You can do it in two lines:
>> s = [a.b];
>> y = [s.c]
y =
4 5 7
Another possible one-liner (less readable!):
>> y = squeeze(cell2mat( struct2cell([a.b]) ))
y =
4
5
7

a.b returns multiple outputs, so you can't expect to call a function on it. The best one-liner I can think of without using arrayfun is:
y = subsref([a.b], substruct('.', c));
Note that a.b.c is effectively the same as:
y = subsref(a.b, substruct('.', c))
Which is why it shouldn't work for non-scalar a.

Related

How to assign values to variables in a handle function?

This is simplified but take as an example the following MATLAB function handle:
F = #(x)[x(1)-x(2);x(2)-x(3)]
The system has of course has many solutions. Is it possible to obtain a solution for a function like this one after substituting at least one variable? For example, substituting x(3)=1 the function would become:
G = #(x)[x(1)-x(2);x(2)-1]
And a solution for the other variables can be obtained. I use fsolve and it works quite well for the system of equations I have. Of course what I need to do can be done using the Symbolic Toolbox, but calling it in a big for loop makes it too slow to use for my code.
I'm trying to come up with some code that can return G given F and a set of indices in x to replace with given values.
What you're basically asking to do is have G call F with values in x reassigned based on a logical replacement index index and a set of replacement values values, which is doable but messy since anonymous functions can only have a single executable statement.
The solution is similar to what is done in this answer, but instead of using the functional form for subscripted reference we'll need to use the functional form for subscripted assignment: subsasgn. Here's what it would look like:
F = #(x)[x(1)-x(2); x(2)-x(3)];
values = [3 2 1];
index = logical([0 0 1]);
G = #(x) F(subsasgn(x, struct('type', '()', 'subs', {{index}}), values(index)));
And here's a test:
>> F([3 2 3])
ans =
1
-1
>> F([3 2 1]) % Replace the last element by 1
ans =
1
1
>> G([3 2 3]) % G handles replacing the last element by 1
ans =
1
1

Is there a built-in (no for loop) way to put ranges of a vector into a cell array using a matrix for the indices?

Given:
data = 0:10;
indices = [1 2; 3 7; 2 5];
Is there a one-line way to do this?:
for i = 1:length(indices)
out{i} = data(indices(i,1):indices(i,2))
end
You can do this with arrayfun:
out = arrayfun(#(a,b) data(a:b), indices(:,1), indices(:,2), 'UniformOutput', false);
However, internally arrayfun is still probably just using a for loop, so I wouldn't expect to see any improvement in speed. This syntax simply allows you to write it as a one-liner. A somewhat ugly one-liner.
The following is on
out = arrayfun(#(ii)data(indices(ii,1):indices(ii,2)),1:size(indices,1),'UniformOutput',false)

Is there a version of bsxfun that works on structure arrays?

Why can I do this:
a = [1 2];
b = [3 4];
bsxfun(#(ai,bj) ai + bj, a, b')
% 4 5
% 5 6
But not this:
a = struct('x', {1 2});
b = struct('x', {3 4});
bsxfun(#(ai,bj) ai.x + bj.x, a, b');
% Error using bsxfun
% Operands must be numeric arrays.
And does a replacement function exist that works in both cases?
This may not be a general solution* but for your particular example it is easy to convert your structure array to a numerical array inside bsxfun, by using comma-separated-list generator syntax, and then using your original anonymous function, i.e.
>> bsxfun(#(ai, bj) ai+bj, [a.x], [b.x]')
ans =
4 5
5 6
and this should still leverage the computational efficiency conferred by bsxfun (as opposed to the much slower "repmat+arrayfun" approach, for instance).
*e.g. it might not work as intended if your field contains an array instead of a scalar, since the expansion to a comma-separated-list will be different
A hacky workaround:
function res = bsxfun_struct(f, A, B)
% best way to ensure our output size matches the behaviour of bsxfun is just to call it
dummy = bsxfun(#(ai, bj) ai+bj, zeros(size(A)), zeros(size(B)));
res_size = size(dummy);
% repeat the matrices as needed
Arep = repmat(A, res_size ./ size(A));
Brep = repmat(B, res_size ./ size(B));
% now we can just apply the function pairwise
res = arrayfun(f, Arep, Brep);
end

MATLAB - avoiding loops to create a matrix based on the elements of other vectors

Suppose I have vectors x,y,z, of lengths n,m,l. I want to create a cell matrix Q using the elements of those vectors. Naively one could use a for loop as so:
for i = 1:n
for j = 1:m
for k = 1:l
Q{i,j,k} = someFunction(x(i), y(j), z(k));
end
end
end
Each element of Q is a vector.
Is there a more elegant (and probably less slow) way to do this?
x=[1 2 3 4];
y=[5 6];
z=[7 8 9];
[X Y Z]=meshgrid(x,y,z);
someFunc = #(a,b,c)[a b c]; #% test function; use whatever you want
Q = arrayfun(someFunc,X,Y,Z,'UniformOutput',false);
Q{1,1,1} #% output: [1 5 7]
If someFunction is defined elsewhere, use arrayfun(#someFunction,X,Y,Z); to get a handle to it. (arrayfun uses each element of the arguments as args to the function handle you provide - it, and the related cellfun, are key in avoiding loops.)
With someFunction is designed this way, then it does not look possible.
You should change someFunction to take matrices and return a matrix. Then the problem becomes writing the specific someFunction using matrix operations. Altough a generic solution to the original problem seems not possible, when you consider a specific function (like I suggested here) it can be possible.

Map function in MATLAB?

I'm a little surprised that MATLAB doesn't have a Map function, so I hacked one together myself since it's something I can't live without. Is there a better version out there? Is there a somewhat-standard functional programming library for MATLAB out there that I'm missing?
function results = map(f,list)
% why doesn't MATLAB have a Map function?
results = zeros(1,length(list));
for k = 1:length(list)
results(1,k) = f(list(k));
end
end
usage would be e.g.
map( #(x)x^2,1:10)
The short answer: the built-in function arrayfun does exactly what your map function does for numeric arrays:
>> y = arrayfun(#(x) x^2, 1:10)
y =
1 4 9 16 25 36 49 64 81 100
There are two other built-in functions that behave similarly: cellfun (which operates on elements of cell arrays) and structfun (which operates on each field of a structure).
However, these functions are often not necessary if you take advantage of vectorization, specifically using element-wise arithmetic operators. For the example you gave, a vectorized solution would be:
>> x = 1:10;
>> y = x.^2
y =
1 4 9 16 25 36 49 64 81 100
Some operations will automatically operate across elements (like adding a scalar value to a vector) while others operators have a special syntax for element-wise operation (denoted by a . before the operator). Many built-in functions in MATLAB are designed to operate on vector and matrix arguments using element-wise operations (often applied to a given dimension, such as sum and mean for example), and thus don't require map functions.
To summarize, here are some different ways to square each element in an array:
x = 1:10; % Sample array
f = #(x) x.^2; % Anonymous function that squares each element of its input
% Option #1:
y = x.^2; % Use the element-wise power operator
% Option #2:
y = f(x); % Pass a vector to f
% Option #3:
y = arrayfun(f, x); % Pass each element to f separately
Of course, for such a simple operation, option #1 is the most sensible (and efficient) choice.
In addition to vector and element-wise operations, there's also cellfun for mapping functions over cell arrays. For example:
cellfun(#upper, {'a', 'b', 'c'}, 'UniformOutput',false)
ans =
'A' 'B' 'C'
If 'UniformOutput' is true (or not provided), it will attempt to concatenate the results according to the dimensions of the cell array, so
cellfun(#upper, {'a', 'b', 'c'})
ans =
ABC
A rather simple solution, using Matlab's vectorization would be:
a = [ 10 20 30 40 50 ]; % the array with the original values
b = [ 10 8 6 4 2 ]; % the mapping array
c = zeros( 1, 10 ); % your target array
Now, typing
c( b ) = a
returns
c = 0 50 0 40 0 30 0 20 0 10
c( b ) is a reference to a vector of size 5 with the elements of c at the indices given by b. Now if you assing values to this reference vector, the original values in c are overwritten, since c( b ) contains references to the values in c and no copies.
It seems that the built-in arrayfun doesn't work if the result needed is an array of function:
eg:
map(#(x)[x x^2 x^3],1:10)
slight mods below make this work better:
function results = map(f,list)
% why doesn't MATLAB have a Map function?
for k = 1:length(list)
if (k==1)
r1=f(list(k));
results = zeros(length(r1),length(list));
results(:,k)=r1;
else
results(:,k) = f(list(k));
end;
end;
end
If matlab does not have a built in map function, it could be because of efficiency considerations. In your implementation you are using a loop to iterate over the elements of the list, which is generally frowned upon in the matlab world. Most built-in matlab functions are "vectorized", i. e. it is more efficient to call a function on an entire array, than to iterate over it yourself and call the function for each element.
In other words, this
a = 1:10;
a.^2
is much faster than this
a = 1:10;
map(#(x)x^2, a)
assuming your definition of map.
You don't need map since a scalar-function that is applied to a list of values is applied to each of the values and hence works similar to map. Just try
l = 1:10
f = #(x) x + 1
f(l)
In your particular case, you could even write
l.^2
Vectorizing the solution as described in the previous answers is the probably the best solution for speed. Vectorizing is also very Matlaby and feels good.
With that said Matlab does now have a Map container class.
See http://www.mathworks.com/help/matlab/map-containers.html