Writing a multiplication MATLAB style - matlab

MATLAB's syntax differs somewhat from the traditional DO loops "logic" which iterates over indexes one at a time. With that in mind, what would be a more proper way to write the following, so it runs a bit faster but is still relatively clear for someone not too familiar with MATLAB.
KT = 0.;
for i=1:37
dKT = KTc(i,1) *const2^KTc(i,2) *const3^KTc(i,3) *const4^KTc(i,4) *const5^KTc(i,5);
KT = KT + dKT;
end
sprintf('KT = %f10.8', KT);
KTc is a matrix 37x5
(if it helps, only the (i,1) values are REAL values, the rest are INTEGERs)
All constants are REAL scalars.

Your lines (in the original question) correctly:
KT = 0.;
for i=1:37
dKT = KTc(i,1) *const2^KTc(i,2) *const3^KTc(i,3) *const4^KTc(i,4) *const5^KTc(i,5);
KT = KT + dKT;
end
sprintf('KT = %f10.8', KT);
On the other hand I would suggest
KT = repmat([1; const2; const3; const4; const5], 1, n) .^ KTc;
KT(1,:) = KTc(1,:);
KT = sum(KT(:));
Loops are rarely used in a real matlab-style program. The reason for that is, that although my second solution does more operations, in practice it is quicker due to more optimal caching at the processor, parallelization, and other possible optimizations which are done silently in the background.
UPDATE: (explanation on repmat)
I think repmat is short for "replicate matrix". What it does really is best explained with two typical examples:
v_row=[1 2 3];
repmat(v_row, 2, 1);
%result:
[1 2 3
1 2 3]
v_col=[1;2;3]; % I could also write v_col=v_row';
repmat(v_col, 1, 2);
[1 1
2 2
3 3]
In general repmat does this:
repmat(m, 2, 3);
[m m m
m m m]
% if m=[1 2; 3 4] was the value of m, then
[1 2 1 2 1 2
3 4 3 4 3 4
1 2 1 2 1 2
3 4 3 4 3 4]

Try ...
KT = KT_coeff(1,1:37) .* const1.^KT_coeff(2,1:37) .* const2.^KT_coeff(3,1:37) .* const3.^KT_coeff(4,1:37) .* const4.^KT_coeff(5,1:37);
Given that you know the size of each KT_coeff is 37 in the 2nd dimension, you could simplify this a bit further with by replacing 1:37 with just : above.

I would avoid all these exponentials, taking a log first and then an exp.
% // way cheaper to evaluate
log_KT = log([c1 c2 c3 c4])*KT_coeff(2:end,:);
% // Final exp
KT = KT_coeff(1,:) .* exp(log_KT);
KT = sum(KT);

Related

How to create a symmetric matrix where each row/column is a subset of a known vector [duplicate]

This question already has an answer here:
How do I generate the following matrix and vector from the given input data in MATLAB?
(1 answer)
Closed 5 years ago.
I have a 7*1 vector a = (1:7).'. I want to form a matrix A of size 4*4 from vector a such that the elements of a form the anti-diagonals of matrix A as follows:
A = [1 2 3 4;
2 3 4 5;
3 4 5 6;
4 5 6 7]
I would like this to work for a general a, not just when the elements are consecutive integers.
I appreciate any help.
Setting up the indexing
Adding the two outputs of meshgrid can give the indices:
[x, y] = meshgrid(1:4, 0:3);
x + y;
% ans = [1 2 3 4
% 2 3 4 5
% 3 4 5 6
% 4 5 6 7];
If a was just as in your example you could stop there. Alternatively, use this to index a general vector a. For comparison, I'll use the same example input as rahnema1 did for their method:
a = [4 6 2 7 3 5 1];
[x, y] = meshgrid(1:4, 0:3);
A = a(x + y);
% A = [4 6 2 7
% 6 2 7 3
% 2 7 3 5
% 7 3 5 1]
There are many ways you could create the indices instead of using meshgrid, see the benchmarking functions below for some exampels!
Benchmarking and seven different methods.
Here are some timings for running different methods, including methods using cumsum, repmat, hankel and a simple for loop. This benchmark was done in Matlab 2015b, so takes advantage of Matlab optimisations etc. which the Octave benchmarks in rahnema1's answer might not do. I also use the timeit function which is more robust than tic/toc because it does multiple trials etc.
function benchie()
n = 10000; % (large) square matrix size
a = 1:2*n-1; % array of correct size, could be anything this long
f1 = #() m1(a,n); disp(['bsxfun: ', num2str(timeit(f1))]);
f2 = #() m2(a,n); disp(['cumsum: ', num2str(timeit(f2))]);
f3 = #() m3(a,n); disp(['meshgrid: ', num2str(timeit(f3))]);
f4 = #() m4(a,n); disp(['repmat: ', num2str(timeit(f4))]);
f5 = #() m5(a,n); disp(['for loop: ', num2str(timeit(f5))]);
f6 = #() m6(a,n); disp(['hankel1: ', num2str(timeit(f6))]);
f7 = #() m7(a,n); disp(['hankel2: ', num2str(timeit(f7))]);
end
% Use bsxfun to do broadcasting of addition
function m1(a,n); A = a(bsxfun(#plus, (1:n), (0:n-1).')); end
% Use cumsum to do cumulative vertical addition to create indices
function m2(a,n); A = a(cumsum([(1:n); ones(n-1,n)])); end
% Add the two meshgrid outputs to get indices
function m3(a,n); [x, y] = meshgrid(1:n, 0:n-1); A = a(x + y); end
% Use repmat twice to replicate the meshgrid results, for equivalent one liner
function m4(a,n); A = a(repmat((1:n)',1,n) + repmat(0:n-1,n,1)); end
% Use a simple for loop. Initialise A and assign values to each row in turn
function m5(a,n); A = zeros(n); for ii = 1:n; A(:,ii) = a(ii:ii+n-1); end; end
% Create a Hankel matrix (constant along anti-diagonals) for indexing
function m6(a,n); A = a(hankel(1:n,n:2*n-1)); end
% Create a Hankel matrix directly from elements
function m7(a,n); A = hankel(a(1:n),a(n:2*n-1)); end
Output:
bsxfun: 1.4397 sec
cumsum: 2.0563 sec
meshgrid: 2.0169 sec
repmat: 1.8598 sec
for loop: 0.4953 sec % MUCH quicker!
hankel1: 2.6154 sec
hankel2: 1.4235 sec
So you are best off using rahnema1's suggestion of bsxfun or direct generation of a hankel matrix if you want a one liner, here is a brilliant StackOverflow answer which explains some of bsxfun's advantages: In Matlab, when is it optimal to use bsxfun?
However, the for loop is more than twice as quick! Conclusion: Matlab has lots of neat ways to achieve things like this, sometimes a simple for loop with some appropriate pre-allocation and Matlab's internal optimisations can be the quickest.
You can use hankel:
n= 4;
A= hankel(a(1:n),a(n:2*n-1))
Other solution(expansion/bsxfun):
In MATLAB r2016b /Octave It can be created as:
A = a((1:4)+(0:3).')
In pre r2016b you can use bsxfun:
A = a(bsxfun(#plus,1:4, (0:3).'))
Example input/output
a = [4 6 2 7 3 5 1]
A =
4 6 2 7
6 2 7 3
2 7 3 5
7 3 5 1
Using the benchmark provided by #Wolfie tested in Octave:
_____________________________________
|Method |memory peak(MB)|timing(Sec)|
|=========|===============|===========|
|bsxfun |2030 |1.50 |
|meshgrid |3556 |2.43 |
|repmat |2411 |2.64 |
|hankel |886 |0.43 |
|for loop |886 |0.82 |

Recurring Function with Matrix Input

I believe most functions in MATLAB should be able to receive matrix input and return the output in the form of matrix.
For example sqrt([1 4 9]) would return [1 2 3].
However, when I tried this recurring factorial function:
function k = fact(z)
if z ~= 0
k = z * fact(z-1);
else
k = 1;
end
end
It works perfectly when a number is input into fact. However, when a matrix is input into fact, it returns the matrix itself, without performing the factorial function.
E.g.
fact(3) returns 6
fact([1 2 3]) returns [1 2 3] instead of [1 2 6].
Any help is appreciated. Thank you very much!
Since MATLAB is not known to be good with recursive functions, how about a vectorized approach? Try this for a vector input -
mat1 = repmat([1:max(z)],[numel(z) 1])
mat1(bsxfun(#gt,1:max(z),z'))=1
output1 = prod(mat1,2)
Sample run -
z =
1 2 7
output1 =
1
2
5040
For the sake of answering your original question, here's the annoying loopy code for a vector or 2D matrix as input -
function k1 = fact1(z1)
k1 = zeros(size(z1));
for ii = 1:size(z1,1)
for jj = 1:size(z1,2)
z = z1(ii,jj);
if z ~= 0
k1(ii,jj) = z .* fact1(z-1);
else
k1(ii,jj) = 1;
end
end
end
return
Sample run -
>> fact1([1 2 7;3 2 1])
ans =
1 2 5040
6 2 1
You can use the gamma function to compute the factorial without recursion:
function k = fact(z)
k = gamma(z+1);
Example:
>> fact([1 2 3 4])
ans =
1 2 6 24
Not sure if all of you know, but there is an actual factorial function defined in MATLAB that can take in arrays / matrices of any size, and computes the factorial element-wise. For example:
k = factorial([1 2 3 4; 5 6 7 8])
k =
1 2 6 24
120 720 5040 40320
Even though this post is looking for a recursive implementation, and Divakar has provided a solution, I'd still like to put my two cents in and suggest an alternative. Also, let's say that we don't have access to factorial, and we want to compute this from first principles. What I would personally do is create a cell array that's the same size as the input matrix, but each element in this cell array would be a linear index array from 1 up to the number defined for each location in the original matrix. You would then apply prod to each cell element to compute the factorial. A precondition is that no number is less than 1, and that all elements are integers. As such:
z1 = ... ; %// Define input matrix here
z1_matr = arrayfun(#(x) 1:x, z1, 'uni', 0);
out = cellfun(#prod, z1_matr);
If z1 = [1 2 3 4; 5 6 7 8];, from my previous example, we get the same output with the above code:
out =
1 2 6 24
120 720 5040 40320
This will obviously be slower as there is an arrayfun then cellfun call immediately after, but I figured I'd add another method for the sake of just adding in another method :) Not sure how constructive this is, but I figured I'd add my own method and join Divakar and Luis Mendo :)

"Desort" a vector (undo a sorting)

In Matlab, sort returns both the sorted vector and an index vector showing which vector element has been moved where:
[v, ix] = sort(u);
Here, v is a vector containing all the elements of u, but sorted. ix is a vector showing the original position of each element of v in u. Using Matlab's syntax, u(ix) == v.
My question: How do I obtain u from v and ix?
Of course, I could simply use:
w = zero(size(v));
for i = 1:length(v)
w(ix(i)) = v(i)
end
if nnz(w == u) == length(u)
print('Success!');
else
print('Failed!');
end
But I am having this tip-of-the-tongue feeling that there is a more elegant, single-statement, vectorized way of doing this.
If you are wondering why one would need to do this instead of just using u: I was trying to implement the Benjamini-Hochberg procedure which adjusts each element of the vector based on its position after sorting, but recovering the original order after adjusting was important for me.
The solution is:
w(ix) = v;
This is a valid Matlab operation provided that w is either at least as big as v, or not yet declared.
Example:
>> u = [4 8 10 6 2];
>> [v, ix] = sort(u)
v = 2 4 6 8 10
ix = 5 1 4 2 3
>> u(ix)
ans = 2 4 6 8 10
>> w(ix) = v
w = 4 8 10 6 2
(Apologies for the trivial question-answer, but I realized the solution as I was typing the question, and thought it might be useful to someone.)

Does matrix contain a vector?

I'm looking for a fast / concise way to check whether some matrix contains given vector, e.g.:
bigMatrix = [1 1 1; 2 2 2; 4 4 4; 5 5 5];
someFunction(bigMatrix, [1 1 1]) % = true
someFunction(bigMatrix, [3 3 3]) % = false
Is there such function/operator, or I need a loop?
I would suggest the following solution:
bigMatrix = [1 1 1; 2 2 2; 4 4 4; 5 5 5];
Vec = [2 2 2];
Index = ismember(bigMatrix, Vec, 'rows');
The result?
Index =
0
1
0
0
ismember is an incredibly useful function that checks whether the elements of one set are in another set. Here, I exploit the rows option to force the function to compare rows, rather than individual elements.
UPDATE: On the other hand, it is always worth doing a few speed tests! I just compared the ismember approach to the following alternative method:
N = size(bigMatrix, 1);
Index2 = zeros(N, 1);
for n = 1:N
if all(bigMatrix(n, :) == Vec)
Index2(n) = 1;
end
end
My findings? The size of bigMatrix matters! In particular, if bigMatrix is on the small side (somewhat of a misnomer), then the loop is much faster. The first approach is preferable only when bigMatrix becomes big. Further, the results are also dependent on how many columns bigMatrix has, as well as rows! I suggest you test both approaches for your application and then go with whichever is faster. (EDIT: This was on R2011a)
General Note: I am continually surprised by how much faster Matlab's loops have gotten in the last few years. Methinks vectorized code is no longer the holy grail that it once was.

How to efficiently access/change one item in each row of a matrix

I have a matrix A with size (nr,nc), a vector of column indices B (so B has size (nr,1) and every element in B is an integer between 1 and nc), and I want to do something to every element in A that is of the form A(i,B(i)) for i between 1 and nr, efficiency being the key concern.
For concreteness, say C is a vector of size (nr,1), the goal is to do
for i=1:nr
A(i,B(i))=A(i,B(i))+C(i)
end
more efficiently. The context is usually that nr>>nc (because when nr is large vectorization is efficient for many operations). I have gotten a factor 3 speedup by using an indicator function approach:
for k=1:nc
A(:,k)=A(:,k)+(k==B).*C
end
Are there other ways (more efficient hopefully) to do this?
I guess this is similar to many questions on double-indexing, but it's concretely one I run into all the time.
Use linear indexing:
idx = sub2ind(size(A), 1:nr, B');
A(idx) = A(idx) + C';
or (edited version with one less transpose)
idx = sub2ind(size(A), (1:nr)', B);
A(idx) = A(idx) + C;
One way would be to use linear indexing of the matrix. You will need a vector v holding the offsets of the first element in each line, then index using A(v + B). For example:
>A=[1 2 3; 4 5 6; 7 8 9]
A =
1 2 3
4 5 6
7 8 9
>B = [1 2 3] % we want the 1st element of row 1, 2nd of row 2, 3rd of row 3
>ii = [0 3 6] + B
>a(ii)
1 5 9
Note: As groovingandi had shown, it is also possible (and more readable) to use sub2ind to generate the ii linear indices vector. The idea is essentially the same.