Linspace applied on array [duplicate] - matlab

This question already has an answer here:
Linspace using matrix input matlab
(1 answer)
Closed 4 years ago.
Given an array like a = [ -1; 0; 1];. For each a(i), I need to compute a linearly spaced vector with linspace(min(a(i),0),max(a(i),0),3);, where each linspace-vector should be stored into a matrix:
A = [-1 -0.5 0;
0 0 0;
0 0.5 1];
With a for loop, I can do this like so:
for i=1:3
A(i) = linspace(min(a(i),0),max(a(i),0),3);
end
How can I achieve this without using loops?

The fastest way I can think of is calculating the step-size, construct the vector from that using implicit binary expansion.
a = [ -1; 0; 1];
n = 3;
stepsizes = (max(a,0)-min(a,0))/(n-1);
A = min(a,0) + (0:(n-1)).*stepsizes;
Timeit:
A couple of timeit results using (use timeit(#SO) and remove comments from the blocks to be timed):
function SO()
n = 1e3;
m = 1e5;
a = randi(9,m,1)-4;
% %Wolfie
% aminmax = [min(a, 0), max(a,0)]';
% A = interp1( [0,1], aminmax, linspace(0,1,n) )';
% %Nicky
% stepsizes = (max(a,0)-min(a,0))/(n-1);
% A = min(a,0) + (0:(n-1)).*stepsizes;
% %Loop
% A = zeros(m,n);
% for i=1:m
% A(i,:) = linspace(min(a(i),0),max(a(i),0),n);
% end
%Arrayfun:
A = cell2mat(arrayfun(#(x) linspace(min(x,0),max(x,0),n),a,'UniformOutput',false));
Then the times are:
Wolfie: 2.2243 s
Mine: 0.3643 s
Standard loop: 1.0953 s
arrayfun: 2.6298 s

Take a = [ -1; 0; 1]. Create the min / max array:
aminmax = [min(a, 0), max(a,0)].';
Now use interp1
N = 3; % Number of interpolation points.
b = interp1( [0,1], aminmax, linspace(0,1,N) ).';
>> b =
-1.0000 -0.5000 0
0 0 0
0 0.5000 1.0000

One of the possible solutions is to use arrayfun that applies a function to each element of the array. You also want to convert your results into a matrix, since the output is in the cell array. Since the output of the arrayfun is non-scalar, you have to turn off uniform output.
cell2mat(arrayfun(#(x) linspace(min(x,0),max(x,0),3),a,'UniformOutput',false))
Edit: I performed some testing using tic-toc method on 100000 long arrays. I found out, that the solution with arrayfun takes approx. 1.5 time longer than the one you suggested with for loops.
The fastest approach would be to calculate what you need using matrix-vector operation. For example, if you only need to calculate linspace with 3 elements, you can use something like:
[min(a(:),0), (max(a(:),0)+min(a(:),0))/2 ,max(a(:),0)];
You can generalize this method for any number of elements in linspace function (not necessarily just 3). Note that readability will suffer, as the volume of code will increase:
j=4; % number of elements in each linspace
b=zeros(size(a,1),j); % create a solution matrix of size Nxj
b(:,1)=min(a(:),0); %first row
b(:,end)=max(a(:),0); % last row
temp=b(:,1)+b(:,end); % sum of the first and the last row
for i=2:j-1
b(:,i)=temp*(i-1)/(j-1); % fill in intermediate rows
end
Note, that in this method I loop over the number of elements in each linspace, but not through the array a. With small j (like j=3 in your example) this will work way faster compared to the method with looping over the array a (if you consider large arrays like a=rand(100000,1)).

Related

Add a diagonal of zeros to a matrix in MATLAB

Suppose I have a matrix A of dimension Nx(N-1) in MATLAB, e.g.
N=5;
A=[1 2 3 4;
5 6 7 8;
9 10 11 12;
13 14 15 16;
17 18 19 20 ];
I want to transform A into an NxN matrix B, just by adding a zero diagonal, i.e.,
B=[ 0 1 2 3 4;
5 0 6 7 8;
9 10 0 11 12;
13 14 15 0 16;
17 18 19 20 0];
This code does what I want:
B_temp = zeros(N,N);
B_temp(1,:) = [0 A(1,:)];
B_temp(N,:) = [A(N,:) 0];
for j=2:N-1
B_temp(j,:)= [A(j,1:j-1) 0 A(j,j:end)];
end
B = B_temp;
Could you suggest an efficient way to vectorise it?
You can do this with upper and lower triangular parts of the matrix (triu and tril).
Then it's a 1 line solution:
B = [tril(A,-1) zeros(N, 1)] + [zeros(N,1) triu(A)];
Edit: benchmark
This is a comparison of the loop method, the 2 methods in Sardar's answer, and my method above.
Benchmark code, using timeit for timing and directly lifting code from question and answers:
function benchie()
N = 1e4; A = rand(N,N-1); % Initialise large matrix
% Set up anonymous functions for input to timeit
s1 = #() sardar1(A,N); s2 = #() sardar2(A,N);
w = #() wolfie(A,N); u = #() user3285148(A,N);
% timings
timeit(s1), timeit(s2), timeit(w), timeit(u)
end
function sardar1(A, N) % using eye as an indexing matrix
B=double(~eye(N)); B(find(B))=A.'; B=B.';
end
function sardar2(A,N) % similar to sardar1, but avoiding slow operations
B=1-eye(N); B(logical(B))=A.'; B=B.';
end
function wolfie(A,N) % using triangular parts of the matrix
B = [tril(A,-1) zeros(N, 1)] + [zeros(N,1) triu(A)];
end
function user3285148(A, N) % original looping method
B = zeros(N,N); B(1,:) = [0 A(1,:)]; B(N,:) = [A(N,:) 0];
for j=2:N-1; B(j,:)= [A(j,1:j-1) 0 A(j,j:end)]; end
end
Results:
Sardar method 1: 2.83 secs
Sardar method 2: 1.82 secs
My method: 1.45 secs
Looping method: 3.80 secs (!)
Conclusions:
Your desire to vectorise this was well founded, looping is way slower than other methods.
Avoiding data conversions and find for large matrices is important, saving ~35% processing time between Sardar's methods.
By avoiding indexing all together you can save a further 20% processing time.
Generate a matrix with zeros at diagonal and ones at non-diagonal indices. Replace the non-diagonal elements with the transpose of A (since MATLAB is column major). Transpose again to get the correct order.
B = double(~eye(N)); %Converting to double since we want to replace with double entries
B(find(B)) = A.'; %Replacing the entries
B = B.'; %Transposing again to get the matrix in the correct order
Edit:
As suggested by Wolfie for the same algorithm, you can get rid of conversion to double and the use of find with:
B = 1-eye(N);
B(logical(B)) = A.';
B = B.';
If you want to insert any vector on a diagonal of a matrix, one can use plain indexing. The following snippet gives you the indices of the desired diagonal, given the size of the square matrix n (matrix is n by n), and the number of the diagonal k, where k=0 corresponds to the main diagonal, positive numbers of k to upper diagonals and negative numbers of k to lower diagonals. ixd finally gives you the 2D indices.
function [idx] = diagidx(n,k)
% n size of square matrix
% k number of diagonal
if k==0 % identity
idx = [(1:n).' (1:n).']; % [row col]
elseif k>0 % Upper diagonal
idx = [(1:n-k).' (1+k:n).'];
elseif k<0 % lower diagonal
idx = [(1+abs(k):n).' (1:n-abs(k)).'];
end
end
Usage:
n=10;
k=3;
A = rand(n);
idx = diagidx(n,k);
A(idx) = 1:(n-k);

Alternating summation without for-loop in MATLAB

I want to create the following vector in MATLAB without using a for-loop. I'm looking for a simple and elegant solution without all kinds of if statements.
[x,y, (x+y)+x, ((x+y)+x)+y, (((x+y)+x)+y)+x]
you can use vector multiplication:
% x, y values
x = 3;
y = 1;
% number of repetitions
n = 2;
% generate times vector for x
timesx = repmat(1:n,[2 1]);
timesx = [timesx(:);n+1];
timesx(2) = 0;
% generate times vector for y
timesy = repmat(1:n,[2 1]);
timesy = [0;timesy(:)];
% sum
s = x*timesx + y*timesy;
and you get:
s =
[3 1 7 8 11]
In MATLAB >2015a
x=3;
y=1;
N=7;
rept=repelem(1:N,2);
res=[x,y,x*rept(3:3+N-3)+y*rept(2:2+N-3)]
There is maybe a way of adding that repelem inside the res line but I do not have MATLAB 2015a or bigger and I can not test this further.
Codegolfing in MATLAB? 39 chars (courtesy to Luis Mendo, introducing t):
x=3;y=1;n=7;
t=(1:n)/2;f=ceil(t)*x+floor(t)*y;f(2)=y

Extracting and storing non-zero entries in MATLAB

Could anyone help me build and correct my code which aims to only save the non-zero elements of an arbitrary square matrix and its index? Basically I need to write a script that does the same function as 'sparse' in MATLAB.
`%Consider a 3x3 matrix
A=[ 0 0 9 ;-1 8 0;0 -5 0 ];
n=3; %size of matrix
%initialise following arrays:
RI= zeros(n,1); %row index
CI = zeros(n,1); %column index
V = zeros(n,1); %value in the matrix
for k = 1:n %row 1 to n
for j = 1:n %column 1 to n
if A(k,j)~=0
RI(k)=k;
CI(j)=j;
V(k,j)=A(k,j);
end
end
end`
You could use the find function to find all the non-zero elements.
So,
[RI, CI, V] = find(A);
% 2 1 -1
% 2 2 8
% 3 2 -5
% 1 3 9
EDIT :
I realize from your comments that your goal was to learn coding in Matlab and you might be wondering why your code didn't work as expected. So let me try to explain the issue along with an example code that is similar to yours.
% Given:
A=[ 0 0 9 ;-1 8 0;0 -5 0 ];
Firstly, instead of manually specifying the size as n = 3, I'd recommend using the built-in size function.
sz = size(A);
% note that this contains 2 elements:
% [number of rows, number of columns]
Next, to initialize the arrays RI, CI and V we would like to know their sizes. Since we do not know the number of non-zero elements to start with, we
have two options: (1) choose a large number that is guaranteed to be equal to or greater than the number of non-zero elements, for example prod(sz). (Why is that true?). (2) Do not initialize it at all and let Matlab dynamically allocate memory as required. I'd follow the second option in the code below.
% we'll keep a count of non-zero elements as we find them
numNZ = 0; % this will increment every time a non-zero element is found
for iCol = 1:sz(2) %column 1 to end
for iRow = 1:sz(1) %row 1 to end
if A(iRow,iCol)~=0
numNZ = numNZ + 1;
RI(numNZ) = iRow;
CI(numNZ) = iCol;
V(numNZ) = A(iRow,iCol);
end
end
end
disp([RI, CI, V])
% 2 1 -1
% 2 2 8
% 3 2 -5
% 1 3 9
Makes sense?
So I think we've established that the point of this is to learn an unfamiliar programming language. The simplest solution is to use sparse itself but that gives you no insight into programming. Nor does find, which can be used similarly.
Now, we could go the same route you've started using: procedural for and if over each row and each column. Could be almost any programming language, but for a few quirks of punctuation. But what you'll find, even if you do correct the mistakes (like the fact that n should be the number of non-zero entries, not the number of rows) is that this is a very slow way of doing numerical work in Matlab.
Here's another (still inefficient, but less so) way which will hopefully provide some insight into the "vectorized" way of doing things, which is one of the things that makes Matlab as powerful as it is:
function [RI, CI, V] = mysparse(A) % first: use functions!
[nRows, nCols] = size(A);
[allRowIndices, allColIndices] = ndgrid(1:nRows, 1:nCols) % let's leave the semicolon off so you can see for yourself what it does.
% It's very similar to `meshgrid` which you'll see more often (it's heavily used in Matlab graphics)
% but `ndgrid` is "simpler" in that it's more in tune with the fundamental conventions of Matlab (rows, then columns)
isNonZero = A ~= 0; % this gives you a "logical array" which is a very powerful thing: it can be used as a subscript to select elements from another array, in one shot...
RI = allRowIndices(isNonZero); % like this
CI = allColIndices(isNonZero); % or this
V = A(isNonZero); % or even this
RI = RI(:); % have to do this explicitly, because the lines above will reshape the values into a single long string under some circumstances but not others
CI = CI(:);
V = V(:);
I will go with a N x 3 matrix where N are the number of non-zero elements in the matrix.
% Define a matrix A as follows:
A = randi([0 1],[4 4])
for i=1:16
if A(i) ~= 0
A(i) = rand;
end
end
[row,col] = find(A);
elms = A(A~=0); % MATLAB always works in column-major order and is consistent,
% so no need to use sub2ind to access elements given by find
newSparse_A = [row col elms];
Output:
newSparse_A =
1.0000 1.0000 0.9027
2.0000 1.0000 0.9448
3.0000 1.0000 0.4909
1.0000 2.0000 0.4893
2.0000 2.0000 0.3377
4.0000 2.0000 0.9001
>> sparse(A)
ans =
(1,1) 0.9027
(2,1) 0.9448
(3,1) 0.4909
(1,2) 0.4893
(2,2) 0.3377
(4,2) 0.9001

Matlab: store array in matrix?

I have many array (n*1 dimension), how can I do something like
matrix = [];
for i = 1:5
for j =1:5
matrix (i,j) = zeros(n,1); % store a given array to a cell of a matrix
end
end
I find Array of Matrices in MATLAB
But this is store matrices into array, not the otherwise.
Ying Xiong's suggestion is what you want if the vectors are of different lengths. But assuming the number of elements is constant (which they seem to be) you may also use a 3-dimensional array, where each (i,j) element contains a vector in the third dimension, like this:
rows = 5; cols = 5; n = 10; %// Dimensions
matrix = zeros(rows, cols, n); %// Initialize matrix
vector = 1:n; %// Just an example
for ii = 1:rows %// Bad practice to use i as a variable name
for jj = 1:cols %// Bad practice to use j as a variable name
matrix(ii,jj,:) = vector; %// Assignment
end
end
Now each index (i,j) contains the vectors you want, for instance:
squeeze(matrix(1,1,:))
ans =
1
2
3
4
5
6
7
8
9
10
Having all values in a single matrix can be a good thing if you want to do similar operations on all elements, as vectorized approaches are usually very fast in MATLAB. You might want to check out permute, reshape and functions like bsxfun.
Note that you might be able to vectorize the loops, but without knowing the specifics, that's impossible to know.
You need to use cell array.
n = 10;
matrix = cell(5,5);
for i = 1:5
for j = 1:5
matrix{i,j} = zeros(n,1);
end
end

How to solve a system of equations involving a tridiagonal matrix? MATLAB

I'm not sure if this typical behaviour or not but I am solving a finite-difference problem using the backward differencing method.
I populated a sparse matrix with the appropriate diagonal terms (along the central diagonal and one above and below it) and I attempted to solve the problem using MATLAB's built-in method (B=A\x) and it seems MATLAB just gets it wrong.
Furthermore, if use inv() and use the inverse of the tridiagonal matrix I get the correct solution.
Why does this behave like this?
Additional info:
http://pastebin.com/AbuEW6CR (Values are tabbed so its easier read)
Stiffness matrix K:
1 0 0 0
-0.009 1.018 -0.009 0
0 -0.009 1.018 -0.009
0 0 0 1
Values for d:
0
15.55
15.55
86.73
Built-in output:
-1.78595556155136e-05
0.00196073713853244
0.00196073713853244
0.0108149483252210
Output using inv(K):
0
15.42
16.19
86.73
Manual output:
0
15.28
16.18
85.16
Code
nx = 21; %number of spatial steps
nt = 501; %number of time steps (varies between 501 and 4001)
p = alpha * dt / dx^2; %arbitrary constant
a = [0 -p*ones(1,nx-2) 0]'; %diagonal below central diagonal
b = (1+2*p)*ones(nx,1); %central diagonal
c = [1 -p*ones(1,nx-2) 1]'; %diagonal above central diagonal
d = zeros(nx, 1); %rhs values
% Variables a,b,c,d are used for the manual tridiagonal method for
% comparison with MATLAB's built-in functions. The variables represent
% diagonals and the rhs of the matrix
% The equation is K*U(n+1)=U(N)
U = zeros(nx,nt);
% Setting initial conditions
U(:,[1 2]) = (60-32)*5/9;
K = sparse(nx,nx);
% Indices of the sparse matrix which correspond to the diagonal
diagonal = 1:nx+1:nx*nx;
% Populating diagonals
K(diagonal) =1+2*p;
K(diagonal(2:end)-1) =-p;
K(diagonal(1:end-1)+1) =-p;
% Applying dirichlet condition at final spatial step, the temperature is
% derived from a table for predefined values during the calculation
K(end,end-1:end)=[0 1];
% Applying boundary conditions at first spatial step
K(1,1:2) = [1 0];
% Populating rhs values and applying boundary conditions, d=U(n)
d(ivec) = U(ivec,n);
d(nx) = R; %From table
d(1) = 0;
U(:,n+1) = tdm(a,b,c,d); % Manual solver, gives correct answer
U(:,n+1) = d\K; % Built-in solver, gives wrong answer
The following line:
U(:,n+1) = d\K;
should have been
U(:,n+1) = K\d;
By mistake I had them the wrong way round and didn't notice it, it obviously changes the mathematical expression and hence the wrong answers.