Replicate vectors shifting them to the right - matlab

In Matlab, I have two single row (1x249) vectors in a 2x249 matrix and I have to create a matrix A by replicating them many times, each time shifting the vectors of 2 positions to the right. I would like to fill the entries on the left with zeros. Is there a smart way to do this? Currently, I am using a for loop and circshift, and I add at each iteration I add the new row to A, but probably this is highly inefficient.
Code (myMat is the matrix I want to shift):
A = [];
myMat = [1 0 -1 zeros(1,246); 0 2 0 -2 zeros(1,245)];
N = 20;
for i=1:N-1
aux = circshift(myMat,[0,2*(i-1)]);
aux(:,1:2*(i-1)) = 0;
A =[A; aux];
end

As you are probably aware, loops in Matlab are not so efficient.
I know that the Mathworks keep saying this is no longer so with JIT
compilation, but I haven't experienced the fast loops yet.
I put your method for constructiong the matrix A in a function:
function A = replvector1(myMat,shift_right,width,N)
pre_alloc = true; % make implementation faster using pre-allocation yes/no
% Pad myMat with zeros to make it wide enough
myMat(1,width)=0;
% initialize A
if pre_alloc
A = zeros(size(myMat,1)*(N-1),width);
else
A = [];
end
% Fill A
for i=1:N-1
aux = circshift(myMat,[0,shift_right*(i-1)]);
aux(:,1:min(width,shift_right*(i-1))) = 0;
A(size(myMat,1)*(i-1)+1:size(myMat,1)*i,:) =aux;
end
Your matrix-operation looks a lot like a kronecker product, but the
block-matrixces have overlapping column ranges so a direct kronecker product
will not work. Instead, I constructed the following function:
function A = replvector2(myMat,shift_right,width,N)
[i,j,a] = find(myMat);
i = kron(ones(N-1,1),i) + kron([0:N-2]',ones(size(i))) * size(myMat,1);
j = kron(ones(N-1,1),j) + kron([0:N-2]',ones(size(j))) * shift_right;
a = kron(ones(N-1,1),a);
ok = j<=width;
A = full(sparse(i(ok),j(ok),a(ok),(N-1)*size(myMat,1),width));
You can follow the algorithm by removing semicolons and looking at intermediate
results.
The following main program runs your example, and can easily be modified to
run similar examples:
% inputs (you may vary them to see that it always works)
shift_right = 2;
width = 249;
myMat1 = [ 1 0 -1 0 ;
0 2 0 -2 ];
N = 20;
% Run your implementation
tic;
A = replvector1(myMat,shift_right,width,N);
disp(sprintf('\n original implementation took %e sec',toc))
% Run the new implementation
tic;
B = replvector2(myMat,shift_right,width,N);
disp(sprintf(' new implementation took %e sec',toc))
disp(sprintf('\n norm(B-A)=%e\n',norm(B-A)))

I've taken Nathan's code (see his answer to this question), and added another possible implementation (replvector3).
My idea here stems from you not really needing a circular shift. You need to right-shift and add zeros to the left. If you start with a pre-allocated array (this is really where the big wins in time are for you, the rest is peanuts), then you already have the zeros. Now you just need to copy over myMat to the right locations.
These are the times I see (MATLAB R2017a):
OP's, with pre-allocation: 1.1730e-04
Nathan's: 5.1992e-05
Mine: 3.5426e-05
^ shift by one on purpose, to make comparison of times easier
This is the full copy, copy-paste into an M-file and run:
function so
shift_right = 2;
width = 249;
myMat = [ 1 0 -1 0 ;
0 2 0 -2 ];
N = 20;
A = replvector1(myMat,shift_right,width,N);
B = replvector2(myMat,shift_right,width,N);
norm(B(:)-A(:))
C = replvector3(myMat,shift_right,width,N);
norm(C(:)-A(:))
timeit(#()replvector1(myMat,shift_right,width,N))
timeit(#()replvector2(myMat,shift_right,width,N))
timeit(#()replvector3(myMat,shift_right,width,N))
% Original version, modified to pre-allocate
function A = replvector1(myMat,shift_right,width,N)
% Assuming width > shift_right * (N-1) + size(myMat,2)
myMat(1,width) = 0;
M = size(myMat,1);
A = zeros(M*(N-1),width);
for i = 1:N-1
aux = circshift(myMat,[0,shift_right*(i-1)]);
aux(:,1:shift_right*(i-1)) = 0;
A(M*(i-1)+(1:M),:) = aux;
end
% Nathan's version
function A = replvector2(myMat,shift_right,width,N)
[i,j,a] = find(myMat);
i = kron(ones(N-1,1),i) + kron((0:N-2)',ones(size(i))) * size(myMat,1);
j = kron(ones(N-1,1),j) + kron((0:N-2)',ones(size(j))) * shift_right;
a = kron(ones(N-1,1),a);
ok = j<=width;
A = full(sparse(i(ok),j(ok),a(ok),(N-1)*size(myMat,1),width));
% My trivial version with loops
function A = replvector3(myMat,shift_right,width,N)
% Assuming width > shift_right * (N-1) + size(myMat,2)
[M,K] = size(myMat);
A = zeros(M*(N-1),width);
for i = 1:N-1
A(M*(i-1)+(1:M),shift_right*(i-1)+(1:K)) = myMat;
end

Related

Matlab function for cumulative power

Is there a function in MATLAB that generates the following matrix for a given scalar r:
1 r r^2 r^3 ... r^n
0 1 r r^2 ... r^(n-1)
0 0 1 r ... r^(n-2)
...
0 0 0 0 ... 1
where each row behaves somewhat like a power analog of the CUMSUM function?
You can compute each term directly using implicit expansion and element-wise power, and then apply triu:
n = 5; % size
r = 2; % base
result = triu(r.^max((1:n)-(1:n).',0));
Or, maybe a little faster because it doesn't compute unwanted powers:
n = 5; % size
r = 2; % base
t = (1:n)-(1:n).';
u = find(t>=0);
t = t(u);
result = zeros(n);
result(u) = r.^t;
Using cumprod and triu:
% parameters
n = 5;
r = 2;
% Create a square matrix filled with 1:
A = ones(n);
% Assign the upper triangular part shifted by one with r
A(triu(A,1)==1)=r;
% cumprod along the second dimension and get only the upper triangular part
A = triu(cumprod(A,2))
Well, cumsum accumulates the sum of a vector but you are asking for a specially design matrix, so the comparison is a bit problematic....
Anyway, it might be that there is a function for this if this is a common special case triangular matrix (my mathematical knowledge is limited here, sorry), but we can also build it quite easily (and efficiently=) ):
N = 10;
r = 2;
% allocate arry
ary = ones(1,N);
% initialize array
ary(2) = r;
for i = 3:N
ary(i) = ary(i-1)*r;
end
% build matrix i.e. copy the array
M = eye(N);
for i = 1:N
M(i,i:end) = ary(1:end-i+1);
end
This assumes that you want to have a matrix of size NxN and r is the value that you want calculate the power of.
FIX: a previous version stated in line 13 M(i,i:end) = ary(i:end);, but the assignment needs to start always at the first position of the ary

MATLAB — How to eliminate equal matrices that are created randomly inside loop?

The code segment I'm working on is given below:
NphaseSteps = 6;
phases = exp( 2*pi*1i * (0:(NphaseSteps-1))/NphaseSteps );
i = 1;
while i <= 10 %number of iterations
ind = randi([1 NphaseSteps],10,10);
inField{i} = phases(ind);
save('inField.mat', 'inField')
i = i + 1;
end
Now, what I want is to keep track of these randomly created matrices "inField{i}" and eliminate the ones that are equal to each other. I know that I can use "if" condition but since I'm new to programming I don't know how to use it more efficiently so that it doesn't take too much time. So, I need your help for a fast working program that does the job. Thanks in advance.
My actual code segment (after making the changes suggested by #bisherbas) is the following. Note that I actually want to use the variable "inField" inside the loop for every random created matrix and the loop advances only if the result satisfies a specific condition. So, I think the answer given by #bisherbas doesn't really eliminate the equal inField matrices before they are used in the calculation. This is, of course, my fault since I didn't declare that in the beginning.
NphaseSteps = 6;
phases = exp( 2*pi*1i * (0:(NphaseSteps-1))/NphaseSteps );
nIterations = 5;
inField = cell(1,nIterations);
i = 1;
j = 1;
while i <= nIterations % number of iterations
ind = randi([1 NphaseSteps],TMsize,TMsize);
tmp = phases(ind);
idx = cellfun(#(x) isequal(x,tmp),inField);
if ~any(idx)
inField{i} = tmp;
end
j = j+1;
outField{i} = TM * inField{i};
outI = abs(outField{i}).^2;
targetIafter{i} = abs(outField{i}(focusX,focusY)).^2;
middleI = targetIafter{i} / 2;
if (max(max(outI)) == targetIafter{i})...
&& ( sum(sum((outI > middleI).*(outI < max(max(outI))))) == 0 )
save('inFieldA.mat', 'inField')
i = i + 1;
end
if mod(j-1,10^6) == 0
fprintf('The number of random matrices tried is: %d million \n',(j-1)/10^6)
end
end
Additionally, I've written a seemingly long expression for my loop condition:
if (max(max(outI)) == targetIafter{i})...
&& ( sum(sum((outI > middleI).*(outI < max(max(outI))))) == 0 )
save('inFieldA.mat', 'inField')
i = i + 1;
end
Here I want a maximum element at some point (focusX, focusY) in the outField matrix. So the first condition decides whether the focus point has the maximum element for the matrix. But I additionally want all other elements to be smaller than a specific number (middleI) and that's why the second part of the if condition is written. However, I'm not very comfortable with this second condition and I'm open to any helps.
Try this:
NphaseSteps = 6;
phases = exp( 2*pi*1i * (0:(NphaseSteps-1))/NphaseSteps );
i = 1;
inField = cell(1,NphaseSteps);
while i <= NphaseSteps %number of iterations
ind = randi([1 NphaseSteps],NphaseSteps,NphaseSteps);
tmp = phases(ind);
idx = cellfun(#(x) isequal(x,tmp),inField);
if ~any(idx)
inField{i} = tmp;
end
save('inField.mat', 'inField')
i = i + 1;
end
Read more on cellfun here:
https://www.mathworks.com/help/matlab/ref/cellfun.html

How can I speed up this MATLAB code with a whileloop?

I'm using a code that calculates expectation value of probabilities. This code contains a while-loop that finds all possible combinations and adds up products of probability combinations. However, when the number of elements becomes large(over 40) it takes too much time, and I want to make the code faster.
The code is as follow-
function pcs = combsum(N,K,prbv)
nprbv=1-prbv; %prbv: probability vector
WV = 1:K; % Working vector.
lim = K; % Sets the limit for working index.
inc = 0; % Controls which element of WV is being worked on.
pcs = 0;
stopp=0;
while stopp==0
if logical((inc+lim)-N)
stp = inc; % This is where the for loop below stops.
flg = 0; % Used for resetting inc.
else
stp = 1;
flg = 1;
end
for jj = 1:stp
WV(K + jj - inc) = lim + jj; % Faster than a vector assignment.
end
PV=nprbv;
PV(WV)=prbv(WV);
pcs=prod(PV)+pcs;
inc = inc*flg + 1; % Increment the counter.
lim = WV(K - inc + 1 ); % lim for next run.
if (inc==K)&&(lim==N-K)
stopp=1;
WV = (N-K+1):N;
PV=nprbv;
PV(WV)=prbv(WV);
pcs=prod(PV)+pcs;
end
end
Is there a way to reduce calculation time? I wonder if parallel computing using GPU would help.
I tried to remove dependent variables in a loop for parallel computing, and I made a matrix of possible combinations using 'combnk' function. This worked faster.
nprbv=1-prbv; %prbv : a probability vector
N = 40;
K = 4;
n_combnk = size(combnk(1:N,K),1);
PV_mat = repmat(nprbv,n_combnk,1);
cnt = 0;
tic;
for i = 1:N-K+1
for j = i+1:N-K+2
for k = j+1:N-K+3
for l = k+1:N-K+4
cnt = cnt+1;
PV_mat(cnt,i) = prbv(i);
PV_mat(cnt,j) = prbv(j);
PV_mat(cnt,k) = prbv(k);
PV_mat(cnt,l) = prbv(l);
end
end
end
end
toc;
tic;
pcs_rr = sum(prod(PV_mat,2));
toc;
However, when K value gets larger, an out-of-memory problem happens in building a combination matrix(PV_mat). How can I break up the big matrix into small ones to avoid memory problem?

How to add character to numeric matrix in matlab?

In matlab usually we add a header using fprintf command.
This is a problem when the size of the table depends on the input and when it exceeds a certain range (more than total number of column able to be presented in the command window).
When this occurs the header which we specified previously using the fprintf command will not be compatible with the current output data.
I would like to know is there a way like adding a character string into the 1st row of the matrix during some kind of iteration process. I had tried hardly but still can't find a proper way to solve this issue.
Or it is actually cannot be done in matlab for this purpose.
Eg
clear;clc
A = [2 8 3 1;0 2 -1 4;7 -2 1 2;-1 0 5 2]
B = [-2;4;3;5]
Es = 1e-5
n = length(B);
x = zeros(n,1);
Ea = ones(n,1);
iter = 0;
while max(Ea) >= Es
if iter <= 30
iter = iter + 1;
x_old = x;
for i = 1:n
j = 1:n;
j(i) = [];
x_cal = x;
x_cal(i) = [];
x(i) = (B(i) - sum(A(i,j) * x_cal)) / A(i,i);
end
else
break
end
x_ans(:,iter) = x;
Ea(:,iter) =abs(( x - x_old) ./ x);
end
result = [1:iter; x_ans; Ea]'
for the coding above..how could I add the heading like iteration for 1st column, x1...x2...x3..xn for nth column and error x1..error x2..error xn for another n column. I would like to make this heading can be automated generated based on the input matrix
If the size of the table depends on the input, use a cell array, using c = cell(...).
In each iteration simply call c{i,j} instead of c[i,j].

matlab precision determinant problem

I have the following program
format compact; format short g; clear; clc;
L = 140; J = 77; Jm = 10540; G = 0.8*10^8; d = L/3;
for i=1:500000
omegan=1.+0.0001*i;
a(1,1) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(1,2) = 2; a(1,3) = 0; a(1,4) = 0;
a(2,1) = 1; a(2,2) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(2,3) = 1; a(2,4) = 0;
a(3,1) = 0; a(3,2) = 1; a(3,3) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(3,4) = 1;
a(4,1) = 0; a(4,2) = 0; a(4,3) = 2; a(4,4) = ((omegan^2)*(Jm/(G*J))*d^2)-2;
if(abs(det(a))<1E-10) sprintf('omegan= %8.3f det= %8.3f',omegan,det(a))
end
end
Analytical solution of the above system, and the same program written in fortran gives out values of omegan equal to 16.3818 and 32.7636 (fortran values; analytical differ a little, but they're there somewhere).
So, now I'm wondering ... where am I going wrong with this ? Why is matlab not giving the expected results ?
(this is probably something terribly simple, but it's giving me headaches)
You're looking for too small of determinant values because Matlab is using a different determinant function (or some other reason like something to do with the floating point accuracy involved in the two different methods). I'll show you that Matlab is essentially giving you the correct values and a better way to approach this problem in general.
First, let's take your code and change it slightly.
format compact; format short g; clear; clc;
L = 140; J = 77; Jm = 10540; G = 0.8*10^8; d = L/3;
vals = zeros(1,500000);
for i=1:500000
omegan=1.+0.0001*i;
a(1,1) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(1,2) = 2; a(1,3) = 0; a(1,4) = 0;
a(2,1) = 1; a(2,2) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(2,3) = 1; a(2,4) = 0;
a(3,1) = 0; a(3,2) = 1; a(3,3) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(3,4) = 1;
a(4,1) = 0; a(4,2) = 0; a(4,3) = 2; a(4,4) = ((omegan^2)*(Jm/(G*J))*d^2)-2;
vals(i) = abs(det(a));
if(vals(i)<1E-10)
sprintf('omegan= %8.3f det= %8.3f',omegan,det(a))
end
end
plot(1.+0.0001*(1:500000),log(vals))
All that I've done really is logged the values of the determinant for all values of omegan and plotted the log of those determinant values as a function of omegan. Here is the plot:
You notice three major dips in the graph. Two coincide with your results of 16.3818 and 32.7636, but there is also an additional one which you were missing (probably because your condition of the determinant being less than 1e-10 was too low even for your Fortran code to pick it up). Therefore, Matlab is also telling you that those are the values of omegan that you were looking for, but because of the determinant was determined in a different manner in Matlab, the values weren't the same - not surprising when dealing with badly conditioned matrices. Also, it probably has to do with Fortran using single precision floats as someone else said. I'm not going to look into why they aren't because I don't want to waste my time on that. Instead, let's look at what you are trying to do and try a different approach.
You, as I'm sure you are aware, are trying to find the eigenvalues of the matrix
a = [[-2 2 0 0]; [1 -2 1 0]; [0 1 -2 1]; [0 0 2 -2]];
, set them equal to
-omegan^2*(Jm/(G*J)*d^2)
and solve for omegan. This is how I went about it:
format compact; format short g; clear; clc;
L = 140; J = 77; Jm = 10540; G = 0.8*10^8; d = L/3;
C1 = (Jm/(G*J)*d^2);
a = [[-2 2 0 0]; [1 -2 1 0]; [0 1 -2 1]; [0,0,2,-2]];
myeigs = eig(a);
myeigs(abs(myeigs) < eps) = 0.0;
for i=1:4
sprintf('omegan= %8.3f', sqrt(-myeigs(i)/C1))
end
This gives you all four solutions - not just the two that you had found with your Fortran code (though one of them, zero, was outside of your testing range for omegan ). If you want to go about solving this by checking the determinant in Matlab, as you've been trying to do, then you'll have to play with the value that you're checking the absolute value of the determinant to be less than. I got it to work for a value of 1e-4 (it gave 3 solutions: 16.382, 28.374, and 32.764).
Sorry for such a long solution, but hopefully it helps.
Update:
In my first block of code above, I replaced
vals(i) = abs(det(a));
with
[L,U] = lu(a);
s = det(L);
vals(i) = abs(s*prod(diag(U)));
which is the algorithm that det is supposedly using according to the Matlab docs. Now, I am able to use 1E-10 as the condition and it works. So maybe Matlab isn't calculating the determinant exactly as the docs say? This is kind of disturbing.
New answer:
You can investigate this problem using symbolic equations, which gives me the correct answers:
>> clear all %# Clear all existing variables
>> format long %# Display more digits of precision
>> syms Jm d omegan G J %# Your symbolic variables
>> a = ((Jm*(d*omegan)^2)/(G*J)-2).*eye(4)+... %# Create the matrix a
diag([2 1 1],1)+...
diag([1 1 2],-1);
>> solns = solve(det(a),'omegan') %# Solve for where the determinant is 0
solns =
0
0
(G*J*Jm)^(1/2)/(Jm*d)
-(G*J*Jm)^(1/2)/(Jm*d)
-(2*(G*J*Jm)^(1/2))/(Jm*d)
(2*(G*J*Jm)^(1/2))/(Jm*d)
(3^(1/2)*(G*J*Jm)^(1/2))/(Jm*d)
-(3^(1/2)*(G*J*Jm)^(1/2))/(Jm*d)
>> solns = subs(solns,{G,J,Jm,d},{8e7,77,10540,140/3}) %# Substitute values
solns =
0
0
16.381862247021893
-16.381862247021893
-32.763724494043785
32.763724494043785
28.374217734436371
-28.374217734436371
I think you either just weren't choosing values in your loop close enough to the solutions for omegan or your threshold for how close the determinant is to zero is too strict. When I plug in the given values to a, along with omegan = 16.3819 (which is the closest value to one solution your loop produces), I get this:
>> det(subs(a,{omegan,G,J,Jm,d},{16.3819,8e7,77,10540,140/3}))
ans =
2.765476845475786e-005
Which is still larger in absolute amplitude than 1e-10.
I put this as an answer because I cannot paste this into a comment: Here's how Matlab calculates the determinant. I assume the rounding errors come from calculating the product of multiple diagonal elements in U.
Algorithm
The determinant is computed from the
triangular factors obtained by
Gaussian elimination
[L,U] = lu(A) s = det(L)
%# This is always +1 or -1
det(A) = s*prod(diag(U))