I'm working on a function that takes a 1xn vector x as input and returns a nxn matrix L.
I'd like to speed things up by vectorizing the loops, but there's a catch that puzzles me: loop index b depends on loop index a. Any help would be appreciated.
x = x(:);
n = length(x);
L = zeros(n, n);
for a = 1 : n,
for b = 1 : a-1,
c = b+1 : a-1;
if all(x(c)' < x(b) + (x(a) - x(b)) * ((b - c)/(b-a))),
L(a,b) = 1;
end
end
end
From a quick test, it looks like you are doing something with the lower triangle only. You might be able to vectorize using ugly tricks like ind2sub and arrayfun similar to this
tril_lin_idx = find(tril(ones(n), -1));
[A, B] = ind2sub([n,n], tril_lin_idx);
C = arrayfun(#(a,b) b+1 : a-1, A, B, 'uniformoutput', false); %cell array
f = #(a,b,c) all(x(c{:})' < x(b) + (x(a) - x(b)) * ((b - c{:})/(b-a)));
L = zeros(n, n);
L(tril_lin_idx) = arrayfun(f, A, B, C);
I cannot test it, since I do not have x and I don't know the expected result. I normally like vectorized solutions, but this is maybe pushing it a bit too much :). I would stick to your explicit for-loop, which might be much clearer and which Matlab's JIT should be able to speed up easily. You could replace the if with L(a,b) = all(...).
Edit1
Updated version, to prevent wasting ~ n^3 space on C:
tril_lin_idx = find(tril(ones(n), -1));
[A, B] = ind2sub([n,n], tril_lin_idx);
c = #(a,b) b+1 : a-1;
f = #(a,b) all(x(c(a, b))' < x(b) + (x(a) - x(b)) * ((b - c(a, b))/(b-a)));
L = zeros(n, n);
L(tril_lin_idx) = arrayfun(f, A, B);
Edit2
Slight variant, which does not use ind2sub and which should be more easy to modify in case b would depend in a more complex way on a. I inlined c for speed, it seems that especially calling the function handles is expensive.
[A,B] = ndgrid(1:n);
v = B<A; % which elements to evaluate
f = #(a,b) all(x(b+1:a-1)' < x(b) + (x(a) - x(b)) * ((b - (b+1:a-1))/(b-a)));
L = false(n);
L(v) = arrayfun(f, A(v), B(v));
If I understand your problem correctly, L(a, b) == 1 if for any c with a < c < b, (c, x(c)) is “below” the line connecting (a, x(a)) and (b, x(b)), right?
It is not a vectorization, but I found the other approach. Rather than comparing all c with a < c < b for each new b, I saved the maximum slope from a to c in (a, b), and used it for (a, b + 1). (I tried with only one direction, but I think that using both directions is also possible.)
x = x(:);
n = length(x);
L = zeros(n);
for a = 1:(n - 1)
L(a, a + 1) = 1;
maxSlope = x(a + 1) - x(a);
for b = (a + 2):n
currSlope = (x(b) - x(a)) / (b - a);
if currSlope > maxSlope
maxSlope = currSlope;
L(a, b) = 1;
end
end
end
I don't know your data, but with some random data, the result is the same with original code (with transpose).
An esoteric answer: You could do the calculations for every a,b,c from 1:n, exclude the don't cares, and then do the all along the c dimension.
[a, b, c] = ndgrid(1:n, 1:n, 1:n);
La = x(c)' < x(b) + (x(a) - x(b)) .* ((b - c)./(b-a));
La(b >= a | c <= b | c >= a) = true;
L = all(La, 3);
Though the jit would probably do just fine with the for loops since they do very little.
Edit: still uses all of the memory, but with less maths
[A, B, C] = ndgrid(1:n, 1:n, 1:n);
valid = B < A & C > B & C < A;
a = A(valid); b = B(valid); c = C(valid);
La = true(size(A));
La(valid) = x(c)' < x(b) + (x(a) - x(b)) .* ((b - c)./(b-a));
L = all(La, 3);
Edit2: alternate last line to add the clause that c of no elements is true
L = all(La,3) | ~any(valid,3);
Related
I have a 3D matrix X of size a x b x c.
I want to create a 3D matrix Y in MATLAB as follows:
X = rand(10, 10, 5);
[a, b, c] = size(X);
for i = 1 : c
for j = 1 : a
for k = 1 : b
if j<a && k<b
Y(j, k, i) = X(j+1, k, i) + X(j, k+1, i).^4;
else
Y(j, k, i) = X(a, b, i) + X(a, b, i).^4;
end
end
end
end
How can I do that by avoiding using a lot of for loops? In other words, how can I rewrite the above code in a faster way without using a lot of loops?
Indexing Arrays/Matrices
Below I added a portion to your script that creates an array Z that is identical to array Y by using indexing that covers the equivalent indices and element-wise operations that are indicated by the dot . preceding the operation. Operations such as multiplication * and division / can be specified element-wise as .* and ./ respectively. Addition and subtraction act in the element-wise fashion and do not need the dot .. I also added an if-statement to check that the arrays are the same and that the for-loops and indexing methods give equivalent results. Indexing using end refers to the last index in the corresponding/respective dimension.
Snippet:
Y = zeros(a,b,c);
Y(1:end-1,1:end-1,:) = X(2:end,1:end-1,:) + X(1: end-1, 2:end,:).^4;
Y(end,1:end,:) = repmat(X(a,b,:) + X(a,b,:).^4,1,b,1);
Y(1:end,end,:) = repmat(X(a,b,:) + X(a,b,:).^4,a,1,1);
Full Script: Including Both Methods and Checking
X = rand(10, 10, 5);
[a, b, c] = size(X);
%Initialed for alternative result%
Z = zeros(a,b,c);
%Looping method%
for i = 1 : c
for j = 1 : a
for k = 1 : b
if j < a && k < b
Y(j, k, i) = X(j+1, k, i) + X(j, k+1, i).^4;
else
Y(j, k, i) = X(a, b, i) + X(a, b, i).^4;
end
end
end
end
%Indexing and element-wise method%
Z(1:end-1,1:end-1,:) = X(2:end,1:end-1,:) + X(1: end-1, 2:end,:).^4;
Z(end,1:end,:) = repmat(X(a,b,:) + X(a,b,:).^4,1,b,1);
Z(1:end,end,:) = repmat(X(a,b,:) + X(a,b,:).^4,a,1,1);
%Checking if results match%
if(Z == Y)
fprintf("Matched result\n");
end
Ran using MATLAB R2019b
What would be the best way to simplify a function by getting rid of a loop?
function Q = gs(f, a, b)
X(4) = sqrt((3+2*sqrt(6/5))/7);
X(3) = sqrt((3-2*sqrt(6/5))/7);
X(2) = -sqrt((3-2*sqrt(6/5))/7);
X(1) = -sqrt((3+2*sqrt(6/5))/7);
W(4) = (18-sqrt(30))/36;
W(3) = (18+sqrt(30))/36;
W(2) = (18+sqrt(30))/36;
W(1) = (18-sqrt(30))/36;
Q = 0;
for i = 1:4
W(i) = (W(i)*(b-a))/2;
X(i) = ((b-a)*X(i)+(b+a))/2;
Q = Q + W(i) * f(X(i));
end
end
Is there any way to use any vector-like solution instead of a for loop?
sum is your best friend here. Also, declaring some constants and creating vectors is useful:
function Q = gs(f, a, b)
c = sqrt((3+2*sqrt(6/5))/7);
d = sqrt((3-2*sqrt(6/5))/7);
e = (18-sqrt(30))/36;
g = (18+sqrt(30))/36;
X = [-c -d d c];
W = [e g g e];
W = ((b - a) / 2) * W;
X = ((b - a)*X + (b + a)) / 2;
Q = sum(W .* f(X));
end
Note that MATLAB loves to handle element-wise operations, so the key is to replace the for loop at the end with scaling all of the elements in W and X with those scaling factors seen in your loop. In addition, using the element-wise multiplication (.*) is key. This of course assumes that f can handle things in an element-wise fashion. If it doesn't, then there's no way to avoid the for loop.
I would highly recommend you consult the MATLAB tutorial on element-wise operations before you venture onwards on your MATLAB journey: https://www.mathworks.com/help/matlab/matlab_prog/array-vs-matrix-operations.html
How can I get a proper double form from this symbolic Matrix in MatLab? I've tried everything but I prefer not using feval or inline function as they're not recommended
This is the code to get the matrix
function T = Romberg (a, b, m, f)
T = zeros(m, m);
T = sym(T);
syms f(x) c h;
f(x) = f;
c = (subs(f,a)+subs(f,b)) / 2;
h = b - a;
T(1,1) = h * c;
som = 0 ;
n = 2;
for i = 2 : m
h = h / 2;
for r = 1 : n/2
som = som + subs(f,(a + 2*(r-1)*h));
T(i,1) = h * (c + som);
n = 2*n;
end
end
r = 1;
for j = 2 : m
r = 4*r;
for i = j : m
T(i,j) = (r * T(i, j-1) - T(i-1,j-1)/(r-1));
end
end
end
And with an input like this
Romberg(0, 1, 4, '2*x')
I get a symbolic matrix output with all the
3 * f(3)/2 + f(1)/2 + f(5)/2
I would like to have a double output.
Can you please help me?
Thank you very much in advance!
Suppose I have a m-by-n-by-p matrix "A", each indices stores a real number, now I want to create another matrix "B" and B(i, j, k) = f(A(i, j, k), i, j, k, otherVars), is there a faster way to do it in matlab rather than looping through all the elements? (notice the function requires the index number (i, j, k))
An example is as follows(The actual function f could be more complex):
A = rand(3, 4, 5);
B = zeros(size(A));
C = 10;
for x = 1:size(A, 1)
for y = 1:size(A, 2)
for z = 1:size(A, 3)
B(x, y, z) = A(x,y,z) + x - y * z + C;
end
end
end
I've tried creating a cell "B", and
B{i, j, k} = [A(i, j, k), i, j, k];
I then applied cellfun() to do the parallel computing, but it's even slower than a for-loop over each elements in A.
In my real implementation, function f is much more complex than B = A + X - Y.*Z + C; it takes four scaler values and I don't want to modify it since it's a function written in an external package. Any suggestions?
Vectorize it by building an ndgrid of the appropriate values:
[X,Y,Z] = ndgrid(1:size(A,1), 1:size(A,2), 1:size(A,3));
B = A + X - Y.*Z + C;
I would like to preface this by saying, I know some functions, including RGB2HSI could do this for me, but I would like to do it manually for a deeper understanding.
So my goal here is to change my RGB image to HSI color scheme. The image is in .raw format, and i am using the following formulas on the binary code to try and convert it.
theta = arccos((.5*(R-G) + (R-B))/((R-G).^2 + (R-B).*(G-B)).^.5);
S = 1 - 3./(R + G + B)
I = 1/3 * (R + G + B)
if B <= G H = theta if B > G H = 360 - theta
So far I have tried two different things, that have resulted in two different errors. The first attempted was the following,
for iii = 1:196608
C(iii) = acosd((.5*(R-G) + (R-B))/((R-G).^2 + (R-B).*(G-B)).^.5);
S(iii) = 1 - 3./(R + G + B);
I(iii) = 1/3 * (R + G + B);
end
Now in attempting this I knew it was grossly inefficent, but I wanted to see if it was a viable option. It was not, and the computer ran out of memory and refused to even run it.
My second attempt was this
fid = fopen('color.raw');
R = fread(fid,512*384*3,'uint8', 2);
fseek(fid, 1, 'bof');
G = fread(fid, 512*384*3, 'uint8', 2);
fseek(fid, 2, 'bof');
B = fread(fid, 512*384*3, 'uint8', 2);
fclose(fid);
R = reshape(R, [512 384]);
G = reshape(G, [512 384]);
B = reshape(B, [512 384]);
C = acosd((.5*(R-G) + (R-B))/((R-G).^2 + (R-B).*(G-B)).^.5);
S = 1 - 3./(R + G + B);
I = 1/3 * (R + G + B);
if B <= G
H = B;
if B > G
H = 360 - B;
end
end
H = H/360;
figure(1);
imagesc(H * S * I)
There were several issues with this that I need help with. First of all, the matrix 'C' has different dimensions than S and I so multiplication is impossible, so my first question is, how would I call up each pixel so I could perform the operations on them individually to avoid this dilemma.
Secondly the if loops refused to work, if I put them after "imagesc" nothing would happen, and if i put them before "imagesc" then the computer would not recognize what variable H was. Where is the correct placement of the ends?
Normally, the matrix 'C' have same dimensions as S and I because:
C = acosd((.5*(R-G) + (R-B))/((R-G).^2 + (R-B).*(G-B)).^.5);
should be
C = acosd((.5*(R-G) + (R-B))./((R-G).^2 + (R-B).*(G-B)).^.5);
elementwise division in the middle was missing . Another point is:
if B <= G
H = B;
if B > G
H = 360 - B;
end
end
should be
H = zeros(size(B));
H(find(B <= G)) = B(find(B <= G));
H(find(B > G)) = 360 - B(find(B > G));