Using vectorization to reduce for loops, how to use conditional if? - arrays

I'm working in a Matlab project and I have a function that is working, but I want to optimize it, reducing the number of for loops that I have in my code.
I read about vectorization, I could use it but how would I include the if conditional statement if I have to test every single value at a time?
function [y, zf] = MyFunction(x, b, zi)
y = zeros([length(x) 1]);
for n = 1:length(x)
for k=1:length(zi)
if n<k
y(n) = y(n) + b(k)*zi(length(zi)+(n-k)+1);
else
y(n) = y(n) + b(k)*x(n-k+1);
end
end
end
zf = x(length(x)-length(zi)+1:length(x));
I manage to do the vectorization, but I can't figure how to do the conditional, I get the warning:
Variable 'n' might be set by a nonscalar operator
function [y, zf] = MyFunction(x, b, zi)
y = zeros([length(x) 1]);
n=1:1:length(x); % use of vectorization
for k=1:length(zi)
if n<k % problem with if
y = y + b(k)*zi(length(zi)+(n-k)+1);
else
y = y + b(k)*x(n-k+1);
end
end
zf = x(length(x)-length(zi)+1:length(x));

Currently n is a vector and k is a scalar, and n<k returns a logical vector. If you directly use if, it would be the same as if all(n), which will only return true when everything in that vector is true! That's unexpected behavior.
I don't know if there's a general way to vectorize codes with if. But in your case, I can do it this way.
% use indice to deal with if
for k=1:length(zi)
y(1:k-1)=y(1:k-1)+b(k)*zi(length(zi)+2-k:end);
y(k:end)=y(k:end)+b(k)*x(1:length(x)-k+1);
end
I also notice that actually if you cat zi and x, it's no need to use 2 individual statement.
% assume both zi & x to be column vector
ziandx=[zi;x];
for k=1:length(zi)
y=y+b(k)*ziandx(length(zi)+2-k:length(zi)+length(x)-k+1);
end
Finally, even this for-loop is no need if you use conv. (check the doc for more detail)
ziandx=[zi;x];
s=conv(b(1:length(zi)),ziandx);
y=s(length(zi)+1:length(zi)+length(x))
I recommend you to read all three methods and understand the idea, thus you can do it yourself next time.

Related

Why do I keep getting an error that array indices must be positive?

It says the index in position 1 of the diff function must be a positive integer or a logical value which is it so why am I getting this error? I'm trying to implement the basic Euler method in MATLAB
y=zeros(1,6);
h=0;
x(1)= 0;
y(1)= 0;
i=1;
diff(y,x)= x+y
while h<=1
y(i+1)=y(i) + h*f(x(i))
h=h+0.2;
i=i+1;
end
Edit: Changed it to the code below but it still raises the same error in the line y(i+1)=...
y=zeros(1,6);
x=zeros(1,6);
h=0;
i=1;
g=x+y;
while h<=1
y(i+1)=y(i) + h*g(x(i),y(i));
h=h+0.2;
i=i+1;
end
Approach: I would recommend defining an anonymous function
diffh =#(x,y) x + y; % define this prior to use
to use later inside the loop.
Then changing one line
y(ii+1)=y(ii) + h*diffh(x(ii),y(ii));
should work. I've added the "h" to the end as a convention to remind me this is an anonymous function (see note at end).
% MATLAB R2019a
y = zeros(1,6);
x = zeros(1,6);
h=0;
ii=1;
diffh =#(x,y) x + y;
while h <= 1
y(ii+1)=y(ii) + h*diffh(x(ii),y(ii));
x(ii+1) = x(ii)+h;
h=h+0.2;
ii=ii+1;
end
Side note: I've also changed the index i to ii by convention (though MATLAB doesn't require this). Unless you overwrite their values, both i and j default as the sqrt(-1). You can absolutely use them as indices without issue (provided you don't later need complex or imaginary numbers). To ensure this never becomes an issue, many people just use ii and jj as a convention to preserve the default values.
Note that diff is a MATLAB function itself.
Using i as an index is mostly not a good idea in Matlab, because i is also imaginary number. Perhaps another name could solve the problem.

Numpy for-loop runtime too long

I have a problem with the runtime of my code. The only module that is really slow is my for-loop over every matrix element in a (144, 208)-array.
I have to check every element if the condition is fulfilled and if so, i have to perform several actions like shifting another (144, 208)-array and add it to an existing one.
Is this not changeable or is my implementation way too beginner-like?
Here is my code:
# With this codeblock i am loading a specific image into python and
binarize it
g = Initialization()
b_init = g.initialize_grid(".\\geometries\\1.png")
# this function will modify the matrix m_sp, which i load in as csv.file
def expand_blockavg(x, h, w):
m, n = x.shape
return np.broadcast_to((x/float(h*w))[:, None, :, None], (m, h, n, w)).reshape(m*h, -1)
m_adapt = expand_blockavg(m_sp, 16, 16) / 256
# This is my actual calculation block
for index, x in np.ndenumerate(b_init):
if x == 1:
a = np.asarray(index)
y = np.subtract(a, index_default)
m_shift = shift(m_adapt, (y[0], y[1]), cval=0)
b = np.add(m_shift, b)
SO, the last block (calculation) is what takes so long. I know that the loop has to check 30k elements. But i thought that with numpy it will be faster.
Can some1 tell me if there's potential for optimization or do i have to live with the fact that it'll take so long.
thanks
Iteration in python is very slow compared to vectorized numpy operations.
An immediate optimization is to only iterate over the indices where the matrix is 1, rather than checking each index. Do this with:
indices = np.argwhere(b_init == 1)
for a in indices:
y = np.array(a) - index_default
m_shift = shift(m_adapt, y[:2], cval=0)
b += m_shift
Not knowing the details of shift it’s hard to say if you can vectorize that also. I replaced function calls with equivalent operations which should be faster; np.add etc. are mostly useful when the operation is being selected programmatically.

MATLAB - Equivalent logical indexing leading to two different results

I'm writing a piece of code for submission through an online grader, as showcased below. B is some given array filled any/all integers 1 through K, and I want to extract the corresponding logical indices of matrix X and perform some operations on those elements, to be put into a return array:
for i = 1:K
A = X(B == i, :);
returnArr(i, :) = sum(A) / length(A);
end
This did not pass the grader at all, and so I looked to change my approach, instead indexing array X indirectly via first using the "find" function, as below:
for i = 1:K
C = find(B == i);
returnArr(i,:) = sum(X(C,:)) / length(C);
end
To my surprise, this code passed the grader without any issues. I know there are a plethora of variations between graders, and one might handle a particular function differently than another, but from a MATLAB functionality/coding perspective, what am I missing in terms of discrepancies between the two approaches? Thanks!
I think the problem is that:
length(C) == sum(B == i)
while
length(A) == max([sum(B == i) , size(X , 2)])
In other words, to obtain the same result of the second example with the first one, you should modify it like this:
A = X(B == i , :);
returnArr(i, :) = sum(A) / size(A,1);
The function length returns the length of largest array dimension

How to recursively fill an array with functions?

So I'm trying to write a function to generate Hermite polynomials and it's doing something super crazy ... Why does it generate different elements for h when I start with a different n? So inputting Hpoly(2,1) gives
h = [ 1, 2*y, 4*y^2 - 2]
while for Hpoly(3,1) ,
h = [ 1, 2*y, 4*y^2 - 4, 2*y*(4*y^2 - 4) - 8*y]
( (4y^2 - 2) vs (4y^2 - 4) as a third element here )
also, I can't figure out how to actually evaluate the expression. I tried out = subs(h(np1),y,x) but that did nothing.
code:
function out = Hpoly(n, x)
clc;
syms y
np1 = n + 1;
h = [1, 2*y];
f(np1)
function f(np1)
if numel(h) < np1
f(np1 - 1)
h(np1) = 2*y*h(np1-1) - 2*(n-1)*h(np1-2);
end
end
h
y = x;
out = h(np1);
end
-------------------------- EDIT ----------------------------
So I got around that by using a while loop instead. I wonder why the other way didn't work ... (and still can't figure out how to evaluate the expression other than just plug in x from the very beginning ... I suppose that's not that important, but would still be nice to know...)
Sadly, my code isn't as fast as hermiteH :( I wonder why.
function out = Hpoly(n, x)
h = [1, 2*x];
np1 = n + 1;
while np1 > length(h)
h(end+1) = 2*x*h(end) - 2*(length(h)-1)*h(end-1);
end
out = h(end)
end
Why is your code slower? Recursion is not necessarily of Matlab's fortes so you may have improved it by using a recurrence relation. However, hermiteH is written in C and your loop won't be as fast as it could be because you're using a while instead of for and needlessly reallocating memory instead of preallocating it. hermiteH may even use a lookup table for the first coefficients or it might benefit from vectorization using the explicit expression. I might rewrite your function like this:
function h = Hpoly(n,x)
% n - Increasing sequence of integers starting at zero
% x - Point at which to evaluate polynomial, numeric or symbolic value
mx = max(n);
h = cast(zeros(1,mx),class(x)); % Use zeros(1,mx,'like',x) in newer versions of Matlab
h(1) = 1;
if mx > 0
h(2) = 2*x;
end
for i = 2:length(n)-1
h(i+1) = 2*x*h(i)-2*(i-1)*h(i-1);
end
You can then call it with
syms x;
deg = 3;
h = Hpoly(0:deg,x)
which returns [ 1, 2*x, 4*x^2 - 2, 2*x*(4*x^2 - 2) - 8*x] (use expand on the output if you want). Unfortunately, this won't be much faster if x is symbolic.
If you're only interested in numeric results of the the polynomial evaluated at particular values, then it's best to avoid symbolic math altogether. The function above valued for double precision x will be three to four orders of magnitude faster than for symbolic x. For example:
x = pi;
deg = 3;
h = Hpoly(0:deg,x)
yields
h =
1.0e+02 *
0.010000000000000 0.062831853071796 0.374784176043574 2.103511015993210
Note:
The hermiteH function is R2015a+, but assuming that you still have access to the Symbolic Math toolbox and the Matlab version is R2012b+, you can also try calling MuPAD's orthpoly::hermite. hermiteH used this function under the hood. See here for details on how to call MuPAD functions from Matlab. This function is a bit simpler in that it only returns a single term. Using a for loop:
syms x;
deg = 2;
h = sym(zeros(1,deg+1));
for i = 1:deg+1
h(i) = feval(symengine,'orthpoly::hermite',i-1,x);
end
Alternatively, you can use map to vectorize the above:
deg = 2;
h = feval(symengine,'map',0:deg,'n->orthpoly::hermite(n,x)');
Both return [ 1, 2*x, 4*x^2 - 2].

Returning all the values in the array

For the following function in matlab:
function s = support(x, y)
for i=1:length(x)
if(y(i)~=1)
s = x(i);
end
end
end
I was intending to return all the values that meet the if-statement, but seems that the function only returns the last element that satisfies the `if-statement. How can I return all the values? What modification should I apply?
You can simply write
s = x(y~=1)
This will return all elements in x that satisfy your condition (y~=1). However, if s should be the same size as x and y, then it may make more sense to mask the elements in s where the condition is false, i.e.
s = x;
s(y==1) = NaN
Your s value is not an array so you return only the last value
function s = support(x, y)
j=1;
for i=1:length(x)
if(y(i)~=1)
s(j) = x(i);
j=j+1;
end
end
Note also that for this kind of problem there is a lot of syntax shorcuts in Matlab which are very more efficient and this is the power of Matlab. You could just write:
function s = support(x, y)
s=x(y~=1);
(Look at logical indexing to understand)
use logical indexing to return all elements in x that satisfy the condition y not 1:
s = x( y ~= 1)

Resources